diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
index 063e185a..4e289bd4 100644
--- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs
+++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
@@ -1197,43 +1197,6 @@ public class ConfigHandler
return result;
}
- ///
- /// Get a SOCKS server profile for pre-SOCKS functionality
- /// Used when TUN mode is enabled or when a custom config has a pre-SOCKS port
- ///
- /// Current configuration
- /// Server node that might need pre-SOCKS
- /// Core type being used
- /// A SOCKS profile item or null if not needed
- public static async Task GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
- {
- ProfileItem? itemSocks = null;
- if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
- {
- itemSocks = new ProfileItem()
- {
- CoreType = ECoreType.sing_box,
- ConfigType = EConfigType.SOCKS,
- Address = Global.Loopback,
- Sni = node.Address, //Tun2SocksAddress
- Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)
- };
- }
- else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
- {
- var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
- itemSocks = new ProfileItem()
- {
- CoreType = preCoreType,
- ConfigType = EConfigType.SOCKS,
- Address = Global.Loopback,
- Port = node.PreSocksPort.Value,
- };
- }
- await Task.CompletedTask;
- return itemSocks;
- }
-
///
/// Remove servers with invalid test results (timeout)
/// Useful for cleaning up subscription lists
diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs
index f7ad2285..b7a8b4f5 100644
--- a/v2rayN/ServiceLib/Handler/CoreHandler.cs
+++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs
@@ -1,5 +1,7 @@
using System.Diagnostics;
using System.Text;
+using ServiceLib.Enums;
+using ServiceLib.Models;
namespace ServiceLib.Handler;
@@ -71,28 +73,24 @@ public class CoreHandler
return;
}
- var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
- var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
- if (result.Success != true)
+ // Create launch context and configure parameters
+ var context = new CoreLaunchContext(node, _config);
+ context.AdjustForConfigType();
+ context.AdjustForSplitCore();
+
+ // Start main core
+ if (!await CoreStart(context))
{
- UpdateFunc(true, result.Msg);
return;
}
- UpdateFunc(false, $"{node.GetSummary()}");
- UpdateFunc(false, $"{Utils.GetRuntimeInfo()}");
- UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
- await CoreStop();
- await Task.Delay(100);
-
- if (Utils.IsWindows() && _config.TunModeItem.EnableTun)
+ // Start pre-core if needed
+ if (!await CoreStartPreService(context))
{
- await Task.Delay(100);
- await WindowsUtils.RemoveTunDevice();
+ await CoreStop(); // Clean up main core if pre-core fails
+ return;
}
- await CoreStart(node);
- await CoreStartPreService(node);
if (_process != null)
{
UpdateFunc(true, $"{node.GetSummary()}");
@@ -181,43 +179,153 @@ public class CoreHandler
#region Private
- private async Task CoreStart(ProfileItem node)
+ ///
+ /// Core launch context that encapsulates all parameters required for launching
+ ///
+ private class CoreLaunchContext
{
- var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
- var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
+ public ProfileItem Node { get; set; }
+ public bool SplitCore { get; set; }
+ public ECoreType CoreType { get; set; }
+ public ECoreType? PreCoreType { get; set; }
+ public ECoreType PureEndpointCore { get; set; }
+ public ECoreType SplitRouteCore { get; set; }
+ public bool EnableTun { get; set; }
+ public int PreSocksPort { get; set; }
- var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
- var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true);
- if (proc is null)
+ public CoreLaunchContext(ProfileItem node, Config config)
{
- return;
+ Node = node;
+ SplitCore = config.SplitCoreItem.EnableSplitCore;
+ CoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
+ PureEndpointCore = AppHandler.Instance.GetSplitCoreType(node, node.ConfigType);
+ SplitRouteCore = config.SplitCoreItem.RouteCoreType;
+ EnableTun = config.TunModeItem.EnableTun;
+ PreSocksPort = 0;
+ PreCoreType = null;
}
- _process = proc;
- }
- private async Task CoreStartPreService(ProfileItem node)
- {
- if (_process != null && !_process.HasExited)
+ ///
+ /// Adjust context parameters based on configuration type
+ ///
+ public void AdjustForConfigType()
{
- var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
- var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
- if (itemSocks != null)
+ if (Node.ConfigType == EConfigType.Custom)
{
- var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
- var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
- var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
- if (result.Success)
+ SplitCore = false;
+ CoreType = Node.CoreType ?? ECoreType.Xray;
+ if (Node.PreSocksPort > 0)
{
- var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
- var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
- if (proc is null)
- {
- return;
- }
- _processPre = proc;
+ PreCoreType = EnableTun ? ECoreType.sing_box : AppHandler.Instance.GetCoreType(Node, Node.ConfigType);
+ PreSocksPort = Node.PreSocksPort.Value;
+ }
+ else
+ {
+ EnableTun = false;
+ PreCoreType = null;
}
}
}
+
+ ///
+ /// Adjust split core configuration
+ ///
+ public void AdjustForSplitCore()
+ {
+ if (SplitCore)
+ {
+ PreCoreType = EnableTun ? ECoreType.sing_box : SplitRouteCore;
+ CoreType = PureEndpointCore;
+
+ if (PreCoreType == CoreType)
+ {
+ PreCoreType = null;
+ SplitCore = false;
+ }
+ else
+ {
+ PreSocksPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.split);
+ }
+ }
+ }
+ }
+
+ private async Task CoreStart(CoreLaunchContext context)
+ {
+ var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
+ var result = context.SplitCore
+ ? await CoreConfigHandler.GeneratePureEndpointConfig(context.Node, fileName)
+ : await CoreConfigHandler.GenerateClientConfig(context.Node, fileName);
+
+ if (result.Success != true)
+ {
+ UpdateFunc(true, result.Msg);
+ return false;
+ }
+
+ UpdateFunc(false, $"{context.Node.GetSummary()}");
+ UpdateFunc(false, $"{Utils.GetRuntimeInfo()}");
+ UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
+
+ await CoreStop();
+ await Task.Delay(100);
+
+ if (Utils.IsWindows() && _config.TunModeItem.EnableTun)
+ {
+ await Task.Delay(100);
+ await WindowsUtils.RemoveTunDevice();
+ }
+
+ var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(context.CoreType);
+ var displayLog = context.Node.ConfigType != EConfigType.Custom || context.Node.DisplayLog;
+ var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true);
+
+ if (proc is null)
+ {
+ UpdateFunc(true, ResUI.FailedToRunCore);
+ return false;
+ }
+
+ _process = proc;
+ _config.RunningCoreType = AppHandler.Instance.GetCoreType(context.Node, context.Node.ConfigType);
+ return true;
+ }
+
+ private async Task CoreStartPreService(CoreLaunchContext context)
+ {
+ if (context.PreCoreType == null)
+ {
+ return true; // No pre-core needed, consider successful
+ }
+
+ var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
+ var itemSocks = new ProfileItem()
+ {
+ CoreType = context.PreCoreType,
+ ConfigType = EConfigType.SOCKS,
+ Address = Global.Loopback,
+ Sni = context.EnableTun && Utils.IsDomain(context.Node.Address) ? context.Node.Address : string.Empty, //Tun2SocksAddress
+ Port = context.PreSocksPort
+ };
+
+ var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
+ if (!result.Success)
+ {
+ UpdateFunc(true, result.Msg);
+ return false;
+ }
+
+ var coreInfo = CoreInfoHandler.Instance.GetCoreInfo((ECoreType)context.PreCoreType);
+ var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
+
+ if (proc is null || (_process?.HasExited == true))
+ {
+ UpdateFunc(true, ResUI.FailedToRunCore);
+ return false;
+ }
+
+ _processPre = proc;
+ return true;
}
private void UpdateFunc(bool notify, string msg)