diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 4888ab6a..b12e1f50 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -278,6 +278,30 @@ public class Global "sing_box" ]; + public static readonly List Hysteria2CoreTypes = + [ + "sing_box", + "Hysteria2" + ]; + + public static readonly List TuicCoreTypes = + [ + "sing_box", + "TUIC" + ]; + + public static readonly List SupportSplitConfigTypes = + [ + EConfigType.VMess, + EConfigType.VLESS, + EConfigType.Shadowsocks, + EConfigType.Trojan, + EConfigType.Hysteria2, + EConfigType.TUIC, + EConfigType.WireGuard, + EConfigType.SOCKS, + ]; + public static readonly List DomainStrategies = [ "AsIs", diff --git a/v2rayN/ServiceLib/Handler/AppHandler.cs b/v2rayN/ServiceLib/Handler/AppHandler.cs index ad9a4029..f3b7e449 100644 --- a/v2rayN/ServiceLib/Handler/AppHandler.cs +++ b/v2rayN/ServiceLib/Handler/AppHandler.cs @@ -235,5 +235,16 @@ public sealed class AppHandler return item?.CoreType ?? ECoreType.Xray; } + public ECoreType GetSplitCoreType(ProfileItem profileItem, EConfigType eConfigType) + { + if (profileItem?.CoreType != null) + { + return (ECoreType)profileItem.CoreType; + } + + var item = _config.SplitCoreItem.SplitCoreTypes?.FirstOrDefault(it => it.ConfigType == eConfigType); + return item?.CoreType ?? ECoreType.Xray; + } + #endregion Core Type } diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 6c93d868..063e185a 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -165,6 +165,13 @@ public class ConfigHandler config.SystemProxyItem.SystemProxyExceptions = Utils.IsWindows() ? Global.SystemProxyExceptionsWindows : Global.SystemProxyExceptionsLinux; } + config.SplitCoreItem ??= new() + { + EnableSplitCore = false, + SplitCoreTypes = new List(), + RouteCoreType = ECoreType.Xray + }; + return config; } diff --git a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs index 40c5eacf..0ee2e227 100644 --- a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs @@ -1,3 +1,5 @@ +using ServiceLib.Services.CoreConfig.Minimal; + namespace ServiceLib.Handler; /// @@ -41,6 +43,37 @@ public class CoreConfigHandler return result; } + public static async Task GeneratePureEndpointConfig(ProfileItem node, string? fileName) + { + var config = AppHandler.Instance.Config; + var result = new RetResult(); + + var coreType = AppHandler.Instance.GetSplitCoreType(node, node.ConfigType); + + result = coreType switch + { + ECoreType.sing_box => await new CoreConfigSingboxService(config).GeneratePureEndpointConfig(node), + ECoreType.Xray => await new CoreConfigV2rayService(config).GeneratePureEndpointConfig(node), + ECoreType.hysteria2 => await new CoreConfigHy2Service(config).GeneratePureEndpointConfig(node), + ECoreType.naiveproxy => await new CoreConfigNaiveService(config).GeneratePureEndpointConfig(node), + ECoreType.tuic => await new CoreConfigTuicService(config).GeneratePureEndpointConfig(node), + ECoreType.juicity => await new CoreConfigJuicityService(config).GeneratePureEndpointConfig(node), + ECoreType.brook => await new CoreConfigBrookService(config).GeneratePureEndpointConfig(node), + ECoreType.shadowquic => await new CoreConfigShadowquicService(config).GeneratePureEndpointConfig(node), + _ => throw new NotImplementedException(), + }; + + if (result.Success != true) + { + return result; + } + if (fileName.IsNotEmpty() && result.Data != null) + { + await File.WriteAllTextAsync(fileName, result.Data.ToString()); + } + return result; + } + private static async Task GenerateClientCustomConfig(ProfileItem node, string? fileName) { var ret = new RetResult(); diff --git a/v2rayN/ServiceLib/Models/Config.cs b/v2rayN/ServiceLib/Models/Config.cs index 15996608..c6536f15 100644 --- a/v2rayN/ServiceLib/Models/Config.cs +++ b/v2rayN/ServiceLib/Models/Config.cs @@ -48,6 +48,7 @@ public class Config public List Inbound { get; set; } public List GlobalHotkeys { get; set; } public List CoreTypeItem { get; set; } + public SplitCoreItem SplitCoreItem { get; set; } #endregion other entities } diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index c6254b9f..a8aa8244 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -138,6 +138,14 @@ public class CoreTypeItem public ECoreType CoreType { get; set; } } +[Serializable] +public class SplitCoreItem +{ + public bool EnableSplitCore { get; set; } + public List SplitCoreTypes { get; set; } + public ECoreType RouteCoreType { get; set; } +} + [Serializable] public class TunModeItem { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs index 39e2bc5c..b3b4cef7 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs @@ -21,11 +21,11 @@ public class CoreConfigBrookService return ret; } - //if (node.ConfigType != EConfigType.Brook) - //{ - // ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; - // return ret; - //} + if (node.ConfigType != EConfigType.Brook) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + return ret; + } var processArgs = "client"; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs index 14b6083f..a08b022b 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs @@ -23,11 +23,11 @@ public class CoreConfigJuicityService return ret; } - //if (node.ConfigType != EConfigType.Juicity) - //{ - // ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; - // return ret; - //} + if (node.ConfigType != EConfigType.Juicity) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + return ret; + } var configJsonNode = new JsonObject(); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs index 3ca89444..1d224210 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs @@ -21,11 +21,11 @@ public class CoreConfigShadowquicService return ret; } - //if (node.ConfigType != EConfigType.Shadowquic) - //{ - // ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; - // return ret; - //} + if (node.ConfigType != EConfigType.Shadowquic) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + return ret; + } var configYamlNode = new Dictionary(); diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index 57d8cac7..cf2252d7 100644 --- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -1,6 +1,7 @@ using System.Reactive; using ReactiveUI; using ReactiveUI.Fody.Helpers; +using ServiceLib.Models; namespace ServiceLib.ViewModels; @@ -104,6 +105,21 @@ public class OptionSettingViewModel : MyReactiveObject #endregion CoreType + #region SplitCoreType + + [Reactive] public bool EnableSplitCore { get; set; } + [Reactive] public string SplitCoreType1 { get; set; } + [Reactive] public string SplitCoreType3 { get; set; } + [Reactive] public string SplitCoreType4 { get; set; } + [Reactive] public string SplitCoreType5 { get; set; } + [Reactive] public string SplitCoreType6 { get; set; } + [Reactive] public string SplitCoreType7 { get; set; } + [Reactive] public string SplitCoreType8 { get; set; } + [Reactive] public string SplitCoreType9 { get; set; } + [Reactive] public string RouteSplitCoreType { get; set; } + + #endregion SplitCoreType + public ReactiveCommand SaveCmd { get; } public OptionSettingViewModel(Func>? updateView) @@ -210,14 +226,13 @@ public class OptionSettingViewModel : MyReactiveObject #endregion Tun mode await InitCoreType(); + + await InitSplitCoreItem(); } private async Task InitCoreType() { - if (_config.CoreTypeItem == null) - { - _config.CoreTypeItem = new List(); - } + _config.CoreTypeItem ??= new List(); foreach (EConfigType it in Enum.GetValues(typeof(EConfigType))) { @@ -232,6 +247,7 @@ public class OptionSettingViewModel : MyReactiveObject CoreType = ECoreType.Xray }); } + _config.CoreTypeItem.ForEach(it => { var type = it.CoreType.ToString(); @@ -269,6 +285,87 @@ public class OptionSettingViewModel : MyReactiveObject await Task.CompletedTask; } + private async Task InitSplitCoreItem() + { + _config.SplitCoreItem ??= new() + { + EnableSplitCore = false, + SplitCoreTypes = new List(), + RouteCoreType = ECoreType.Xray + }; + + foreach (EConfigType it in Enum.GetValues(typeof(EConfigType))) + { + if (_config.SplitCoreItem.SplitCoreTypes.FindIndex(t => t.ConfigType == it) >= 0) + { + continue; + } + + if (it is EConfigType.Hysteria2 or EConfigType.TUIC) + { + _config.SplitCoreItem.SplitCoreTypes.Add(new CoreTypeItem() + { + ConfigType = it, + CoreType = ECoreType.sing_box + }); + continue; + } + if (it is EConfigType.Custom) + { + continue; + } + + _config.SplitCoreItem.SplitCoreTypes.Add(new CoreTypeItem() + { + ConfigType = it, + CoreType = ECoreType.Xray + }); + } + + EnableSplitCore = _config.SplitCoreItem.EnableSplitCore; + RouteSplitCoreType = _config.SplitCoreItem.RouteCoreType.ToString(); + + _config.SplitCoreItem.SplitCoreTypes.ForEach(it => + { + var type = it.CoreType.ToString(); + switch ((int)it.ConfigType) + { + case 1: + SplitCoreType1 = type; + break; + + case 3: + SplitCoreType3 = type; + break; + + case 4: + SplitCoreType4 = type; + break; + + case 5: + SplitCoreType5 = type; + break; + + case 6: + SplitCoreType6 = type; + break; + + case 7: + SplitCoreType7 = type; + break; + + case 8: + SplitCoreType8 = type; + break; + + case 9: + SplitCoreType9 = type; + break; + } + }); + await Task.CompletedTask; + } + private async Task SaveSettingAsync() { if (localPort.ToString().IsNullOrEmpty() || !Utils.IsNumeric(localPort.ToString()) @@ -362,6 +459,7 @@ public class OptionSettingViewModel : MyReactiveObject //coreType await SaveCoreType(); + await SaveSplitCoreType(); if (await ConfigHandler.SaveConfig(_config) == 0) { @@ -420,4 +518,46 @@ public class OptionSettingViewModel : MyReactiveObject } await Task.CompletedTask; } + + private async Task SaveSplitCoreType() + { + for (int k = 1; k <= _config.SplitCoreItem.SplitCoreTypes.Count; k++) + { + var item = _config.SplitCoreItem.SplitCoreTypes[k - 1]; + var type = string.Empty; + switch ((int)item.ConfigType) + { + case 1: + type = SplitCoreType1; + break; + case 3: + type = SplitCoreType3; + break; + case 4: + type = SplitCoreType4; + break; + case 5: + type = SplitCoreType5; + break; + case 6: + type = SplitCoreType6; + break; + case 7: + type = SplitCoreType7; + break; + case 8: + type = SplitCoreType8; + break; + case 9: + type = SplitCoreType9; + break; + default: + continue; + } + item.CoreType = (ECoreType)Enum.Parse(typeof(ECoreType), type); + } + _config.SplitCoreItem.RouteCoreType = (ECoreType)Enum.Parse(typeof(ECoreType), RouteSplitCoreType); + _config.SplitCoreItem.EnableSplitCore = EnableSplitCore; + await Task.CompletedTask; + } } diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 890352fc..07d01f53 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -1110,122 +1110,314 @@ - - - - - - - - - + Margin="{StaticResource Margin8}"> + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + Margin="{StaticResource Margin8}"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs index c790e25d..889016bb 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs @@ -43,6 +43,17 @@ public partial class OptionSettingWindow cmbCoreType6.ItemsSource = Global.CoreTypes; cmbCoreType9.ItemsSource = Global.CoreTypes; + cmbCoreSplitRouteType.ItemsSource = Global.CoreTypes; + + cmbCoreSplitType1.ItemsSource = Global.CoreTypes; + cmbCoreSplitType3.ItemsSource = Global.CoreTypes; + cmbCoreSplitType4.ItemsSource = Global.CoreTypes; + cmbCoreSplitType5.ItemsSource = Global.CoreTypes; + cmbCoreSplitType6.ItemsSource = Global.CoreTypes; + cmbCoreSplitType7.ItemsSource = Global.Hysteria2CoreTypes; + cmbCoreSplitType8.ItemsSource = Global.TuicCoreTypes; + cmbCoreSplitType9.ItemsSource = Global.CoreTypes; + cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList(); cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList(); cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls; @@ -131,6 +142,18 @@ public partial class OptionSettingWindow this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.EnableSplitCore, v => v.togCoreSplit.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.RouteSplitCoreType, v => v.cmbCoreSplitRouteType.Text).DisposeWith(disposables); + + this.Bind(ViewModel, vm => vm.SplitCoreType1, v => v.cmbCoreSplitType1.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType3, v => v.cmbCoreSplitType3.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType4, v => v.cmbCoreSplitType4.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType5, v => v.cmbCoreSplitType5.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType6, v => v.cmbCoreSplitType6.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType7, v => v.cmbCoreSplitType7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType8, v => v.cmbCoreSplitType8.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType9, v => v.cmbCoreSplitType9.Text).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);