Add fallback support

This commit is contained in:
DHR60 2025-09-11 15:24:12 +08:00
parent c76d125ce1
commit 2ab044b4fc
21 changed files with 130 additions and 8 deletions

View file

@ -3,6 +3,7 @@ namespace ServiceLib.Enums;
public enum EMultipleLoad
{
LeastPing,
Fallback,
Random,
RoundRobin,
LeastLoad

View file

@ -1199,16 +1199,22 @@ public static class ConfigHandler
{
remark = multipleLoad switch
{
EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerXrayLeastPing,
EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerXrayFallback,
EMultipleLoad.Random => ResUI.menuGenGroupMultipleServerXrayRandom,
EMultipleLoad.RoundRobin => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerXrayLeastPing,
EMultipleLoad.LeastLoad => ResUI.menuGenGroupMultipleServerXrayLeastLoad,
_ => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
};
}
else if (coreType == ECoreType.sing_box)
{
remark = ResUI.menuGenGroupMultipleServerSingBoxLeastPing;
remark = multipleLoad switch
{
EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerSingBoxFallback,
_ => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
};
}
var profile = new ProfileItem
{

View file

@ -145,6 +145,7 @@ public class Outbound4Sbox : BaseServer4Sbox
public string? plugin_opts { get; set; }
public List<string>? outbounds { get; set; }
public bool? interrupt_exist_connections { get; set; }
public int? tolerance { get; set; }
}
public class Endpoints4Sbox : BaseServer4Sbox

View file

@ -987,6 +987,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Multi-Configuration Fallback by sing-box 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerSingBoxFallback {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerSingBoxFallback", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration LeastPing by sing-box 的本地化字符串。
/// </summary>
@ -996,6 +1005,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Multi-Configuration Fallback by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayFallback {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerXrayFallback", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration LeastLoad by Xray 的本地化字符串。
/// </summary>
@ -2598,6 +2616,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Fallback 的本地化字符串。
/// </summary>
public static string TbFallback {
get {
return ResourceManager.GetString("TbFallback", resourceCulture);
}
}
/// <summary>
/// 查找类似 Fingerprint 的本地化字符串。
/// </summary>

View file

@ -1554,4 +1554,13 @@
<data name="menuServerList" xml:space="preserve">
<value>Server List</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>Fallback</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
</root>

View file

@ -1554,4 +1554,13 @@
<data name="menuServerList" xml:space="preserve">
<value>Server List</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>Fallback</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
</root>

View file

@ -1554,4 +1554,13 @@
<data name="menuServerList" xml:space="preserve">
<value>Server List</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>Fallback</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
</root>

View file

@ -1554,4 +1554,13 @@
<data name="menuServerList" xml:space="preserve">
<value>Server List</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>Fallback</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
</root>

View file

@ -1551,4 +1551,13 @@
<data name="menuServerList" xml:space="preserve">
<value>服务器列表</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>故障转移</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>多配置文件故障转移 sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>多配置文件故障转移 Xray</value>
</data>
</root>

View file

@ -1551,4 +1551,13 @@
<data name="menuServerList" xml:space="preserve">
<value>Server List</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>Fallback</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
</root>

View file

@ -36,7 +36,7 @@ public partial class CoreConfigSingboxService(Config config)
switch (node.ConfigType)
{
case EConfigType.PolicyGroup:
return await GenerateClientMultipleLoadConfig(childProfiles);
return await GenerateClientMultipleLoadConfig(childProfiles, profileGroupItem.MultipleLoad);
case EConfigType.ProxyChain:
return await GenerateClientChainConfig(childProfiles);
}
@ -371,7 +371,7 @@ public partial class CoreConfigSingboxService(Config config)
}
}
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds)
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad)
{
var ret = new RetResult();
try
@ -446,7 +446,7 @@ public partial class CoreConfigSingboxService(Config config)
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
await GenOutboundsList(proxyProfiles, singboxConfig);
await GenOutboundsList(proxyProfiles, singboxConfig, multipleLoad);
await GenDns(null, singboxConfig);
await ConvertGeo2Ruleset(singboxConfig);

View file

@ -410,7 +410,7 @@ public partial class CoreConfigSingboxService
return 0;
}
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, SingboxConfig singboxConfig)
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, SingboxConfig singboxConfig, EMultipleLoad multipleLoad)
{
try
{
@ -513,6 +513,11 @@ public partial class CoreConfigSingboxService
interrupt_exist_connections = false,
};
if (multipleLoad == EMultipleLoad.Fallback)
{
outUrltest.tolerance = 5000;
}
// Add selector outbound (manual selection)
var outSelector = new Outbound4Sbox
{

View file

@ -15,7 +15,7 @@ public partial class CoreConfigV2rayService
};
v2rayConfig.observatory = observatory;
}
else if (multipleLoad == EMultipleLoad.LeastLoad)
else if (multipleLoad is EMultipleLoad.LeastLoad or EMultipleLoad.Fallback)
{
var burstObservatory = new BurstObservatory4Ray
{
@ -41,7 +41,14 @@ public partial class CoreConfigV2rayService
var balancer = new BalancersItem4Ray
{
selector = [Global.ProxyTag],
strategy = new() { type = strategyType },
strategy = new()
{
type = strategyType,
settings = new()
{
expected = 1,
},
},
tag = $"{Global.ProxyTag}-round",
};
v2rayConfig.routing.balancers = [balancer];

View file

@ -76,6 +76,7 @@ public class AddGroupServerViewModel : MyReactiveObject
PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
{
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
EMultipleLoad.Fallback => ResUI.TbFallback,
EMultipleLoad.Random => ResUI.TbRandom,
EMultipleLoad.RoundRobin => ResUI.TbRoundRobin,
EMultipleLoad.LeastLoad => ResUI.TbLeastLoad,
@ -206,6 +207,7 @@ public class AddGroupServerViewModel : MyReactiveObject
profileGroup.MultipleLoad = PolicyGroupType switch
{
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
var s when s == ResUI.TbRandom => EMultipleLoad.Random,
var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,

View file

@ -56,7 +56,9 @@ public class ProfilesViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayRoundRobinCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayLeastPingCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayLeastLoadCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayFallbackCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerSingBoxLeastPingCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerSingBoxFallbackCmd { get; }
//servers move
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
@ -154,10 +156,18 @@ public class ProfilesViewModel : MyReactiveObject
{
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.LeastLoad);
}, canEditRemove);
GenGroupMultipleServerXrayFallbackCmd = ReactiveCommand.CreateFromTask(async () =>
{
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.Fallback);
}, canEditRemove);
GenGroupMultipleServerSingBoxLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
{
await GenGroupMultipleServer(ECoreType.sing_box, EMultipleLoad.LeastPing);
}, canEditRemove);
GenGroupMultipleServerSingBoxFallbackCmd = ReactiveCommand.CreateFromTask(async () =>
{
await GenGroupMultipleServer(ECoreType.sing_box, EMultipleLoad.Fallback);
}, canEditRemove);
//servers move
MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>

View file

@ -29,6 +29,7 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
cmbPolicyGroupType.ItemsSource = new List<string>
{
ResUI.TbLeastPing,
ResUI.TbFallback,
ResUI.TbRandom,
ResUI.TbRoundRobin,
ResUI.TbLeastLoad,

View file

@ -106,6 +106,7 @@
<MenuItem x:Name="menuGenGroupMultipleServerXrayLeastLoad" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastLoad}" />
<Separator />
<MenuItem x:Name="menuGenGroupMultipleServerSingBoxLeastPing" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxLeastPing}" />
<MenuItem x:Name="menuGenGroupMultipleServerSingBoxFallback" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxFallback}" />
</MenuItem>
<Separator />
<MenuItem x:Name="menuMixedTestServer" Header="{x:Static resx:ResUI.menuMixedTestServer}" />

View file

@ -71,6 +71,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastPingCmd, v => v.menuGenGroupMultipleServerXrayLeastPing).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastLoadCmd, v => v.menuGenGroupMultipleServerXrayLeastLoad).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxLeastPingCmd, v => v.menuGenGroupMultipleServerSingBoxLeastPing).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxFallbackCmd, v => v.menuGenGroupMultipleServerSingBoxFallback).DisposeWith(disposables);
//servers move
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);

View file

@ -24,6 +24,7 @@ public partial class AddGroupServerWindow
cmbPolicyGroupType.ItemsSource = new List<string>
{
ResUI.TbLeastPing,
ResUI.TbFallback,
ResUI.TbRandom,
ResUI.TbRoundRobin,
ResUI.TbLeastLoad,

View file

@ -147,6 +147,10 @@
x:Name="menuGenGroupMultipleServerSingBoxLeastPing"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxLeastPing}" />
<MenuItem
x:Name="menuGenGroupMultipleServerSingBoxFallback"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxFallback}" />
</MenuItem>
<Separator />
<MenuItem

View file

@ -65,6 +65,7 @@ public partial class ProfilesView
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastPingCmd, v => v.menuGenGroupMultipleServerXrayLeastPing).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastLoadCmd, v => v.menuGenGroupMultipleServerXrayLeastLoad).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxLeastPingCmd, v => v.menuGenGroupMultipleServerSingBoxLeastPing).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxFallbackCmd, v => v.menuGenGroupMultipleServerSingBoxFallback).DisposeWith(disposables);
//servers move
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);