From 6b4ae5a386463bb17a6cd812c71bae6198ccb7a1 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sun, 1 Mar 2026 05:47:48 +0000 Subject: [PATCH] Fix (#8864) * Fix * Build all contexts * Notify validator result * Fix --- .../Builder/CoreConfigContextBuilder.cs | 106 ++++++++++++++++++ v2rayN/ServiceLib/Handler/ConfigHandler.cs | 41 ------- v2rayN/ServiceLib/Manager/CoreManager.cs | 22 ++-- v2rayN/ServiceLib/Manager/NoticeManager.cs | 21 ++++ .../ViewModels/MainWindowViewModel.cs | 21 +--- .../ViewModels/ProfilesViewModel.cs | 26 +---- 6 files changed, 144 insertions(+), 93 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs b/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs index 23a495f8..4894b334 100644 --- a/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs +++ b/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs @@ -5,6 +5,39 @@ public record CoreConfigContextBuilderResult(CoreConfigContext Context, NodeVali public bool Success => ValidatorResult.Success; } +/// +/// Holds the results of a full context build, including the main context and an optional +/// pre-socks context (e.g. for TUN protection or pre-socks chaining). +/// +public record CoreConfigContextBuilderAllResult( + CoreConfigContextBuilderResult MainResult, + CoreConfigContextBuilderResult? PreSocksResult) +{ + /// True only when both the main result and (if present) the pre-socks result succeeded. + public bool Success => MainResult.Success && (PreSocksResult?.Success ?? true); + + /// + /// Merges all errors and warnings from the main result and the optional pre-socks result + /// into a single for unified notification. + /// + public NodeValidatorResult CombinedValidatorResult => new( + [.. MainResult.ValidatorResult.Errors, .. PreSocksResult?.ValidatorResult.Errors ?? []], + [.. MainResult.ValidatorResult.Warnings, .. PreSocksResult?.ValidatorResult.Warnings ?? []]); + + /// + /// The main context with TunProtectSsPort/ProxyRelaySsPort and ProtectDomainList merged in + /// from the pre-socks result (if any). Pass this to the core runner. + /// + public CoreConfigContext ResolvedMainContext => PreSocksResult is not null + ? MainResult.Context with + { + TunProtectSsPort = PreSocksResult.Context.TunProtectSsPort, + ProxyRelaySsPort = PreSocksResult.Context.ProxyRelaySsPort, + ProtectDomainList = [.. MainResult.Context.ProtectDomainList ?? [], .. PreSocksResult.Context.ProtectDomainList ?? []], + } + : MainResult.Context; +} + public class CoreConfigContextBuilder { /// @@ -75,6 +108,79 @@ public class CoreConfigContextBuilder return new CoreConfigContextBuilderResult(context, validatorResult); } + /// + /// Builds the main for and, when + /// the main build succeeds, also builds the optional pre-socks context required for TUN + /// protection or pre-socks proxy chaining. + /// + public static async Task BuildAll(Config config, ProfileItem node) + { + var mainResult = await Build(config, node); + if (!mainResult.Success) + { + return new CoreConfigContextBuilderAllResult(mainResult, null); + } + + var preResult = await BuildPreSocksIfNeeded(mainResult.Context); + return new CoreConfigContextBuilderAllResult(mainResult, preResult); + } + + /// + /// Determines whether a pre-socks context is required for + /// and, if so, builds and returns it. Returns null when no pre-socks core is needed. + /// + private static async Task BuildPreSocksIfNeeded(CoreConfigContext nodeContext) + { + var config = nodeContext.AppConfig; + var node = nodeContext.Node; + var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType); + + var preSocksItem = ConfigHandler.GetPreSocksItem(config, node, coreType); + if (preSocksItem != null) + { + var preSocksResult = await Build(nodeContext.AppConfig, preSocksItem); + return preSocksResult with + { + Context = preSocksResult.Context with + { + ProtectDomainList = [.. nodeContext.ProtectDomainList ?? [], .. preSocksResult.Context.ProtectDomainList ?? []], + } + }; + } + + if (!nodeContext.IsTunEnabled + || coreType != ECoreType.Xray + || node.ConfigType == EConfigType.Custom) + { + return null; + } + + var tunProtectSsPort = Utils.GetFreePort(); + var proxyRelaySsPort = Utils.GetFreePort(); + var preItem = new ProfileItem() + { + CoreType = ECoreType.sing_box, + ConfigType = EConfigType.Shadowsocks, + Address = Global.Loopback, + Port = proxyRelaySsPort, + Password = Global.None, + }; + preItem.SetProtocolExtra(preItem.GetProtocolExtra() with + { + SsMethod = Global.None, + }); + var preResult2 = await Build(nodeContext.AppConfig, preItem); + return preResult2 with + { + Context = preResult2.Context with + { + ProtectDomainList = [.. nodeContext.ProtectDomainList ?? [], .. preResult2.Context.ProtectDomainList ?? []], + TunProtectSsPort = tunProtectSsPort, + ProxyRelaySsPort = proxyRelaySsPort, + } + }; + } + /// /// Resolves a node into the context, optionally wrapping it in a subscription-level proxy chain. /// Returns the effective (possibly replaced) node and the validation result. diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index e5b6fec3..f3ef7bce 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -1321,47 +1321,6 @@ public static class ConfigHandler return itemSocks; } - public static CoreConfigContext? GetPreSocksCoreConfigContext(CoreConfigContext nodeContext) - { - var config = nodeContext.AppConfig; - var node = nodeContext.Node; - var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType); - - var preSocksItem = GetPreSocksItem(config, node, coreType); - if (preSocksItem != null) - { - return nodeContext with { Node = preSocksItem, }; - } - - if ((!nodeContext.IsTunEnabled) - || coreType != ECoreType.Xray - || node.ConfigType == EConfigType.Custom) - { - return null; - } - var tunProtectSsPort = Utils.GetFreePort(); - var proxyRelaySsPort = Utils.GetFreePort(); - var preItem = new ProfileItem() - { - CoreType = ECoreType.sing_box, - ConfigType = EConfigType.Shadowsocks, - Address = Global.Loopback, - Port = proxyRelaySsPort, - Password = Global.None, - }; - preItem.SetProtocolExtra(preItem.GetProtocolExtra() with - { - SsMethod = Global.None, - }); - var preContext = nodeContext with - { - Node = preItem, - TunProtectSsPort = tunProtectSsPort, - ProxyRelaySsPort = proxyRelaySsPort, - }; - return preContext; - } - /// /// Remove servers with invalid test results (timeout) /// Useful for cleaning up subscription lists diff --git a/v2rayN/ServiceLib/Manager/CoreManager.cs b/v2rayN/ServiceLib/Manager/CoreManager.cs index cd30450f..b2df4562 100644 --- a/v2rayN/ServiceLib/Manager/CoreManager.cs +++ b/v2rayN/ServiceLib/Manager/CoreManager.cs @@ -57,27 +57,19 @@ public class CoreManager } } - public async Task LoadCore(CoreConfigContext? context) + /// Resolved main context (with pre-socks ports already merged if applicable). + /// Optional pre-socks context passed to . + public async Task LoadCore(CoreConfigContext? mainContext, CoreConfigContext? preContext) { - if (context == null) + if (mainContext == null) { await UpdateFunc(false, ResUI.CheckServerSettings); return; } - var contextMod = context; - var node = contextMod.Node; + var node = mainContext.Node; var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName); - var preContext = ConfigHandler.GetPreSocksCoreConfigContext(contextMod); - if (preContext is not null) - { - contextMod = contextMod with - { - TunProtectSsPort = preContext.TunProtectSsPort, - ProxyRelaySsPort = preContext.ProxyRelaySsPort, - }; - } - var result = await CoreConfigHandler.GenerateClientConfig(contextMod, fileName); + var result = await CoreConfigHandler.GenerateClientConfig(mainContext, fileName); if (result.Success != true) { await UpdateFunc(true, result.Msg); @@ -96,7 +88,7 @@ public class CoreManager await WindowsUtils.RemoveTunDevice(); } - await CoreStart(contextMod); + await CoreStart(mainContext); await CoreStartPreService(preContext); if (_processService != null) { diff --git a/v2rayN/ServiceLib/Manager/NoticeManager.cs b/v2rayN/ServiceLib/Manager/NoticeManager.cs index da034f02..184ef79c 100644 --- a/v2rayN/ServiceLib/Manager/NoticeManager.cs +++ b/v2rayN/ServiceLib/Manager/NoticeManager.cs @@ -38,4 +38,25 @@ public class NoticeManager Enqueue(msg); SendMessage(msg); } + + /// + /// Sends each error and warning in to the message panel + /// and enqueues a summary snack notification (capped at 10 messages). + /// Returns true when there were any messages so the caller can decide on early-return + /// based on . + /// + public bool NotifyValidatorResult(NodeValidatorResult validatorResult) + { + var msgs = new List([.. validatorResult.Errors, .. validatorResult.Warnings]); + if (msgs.Count == 0) + { + return false; + } + foreach (var msg in msgs) + { + SendMessage(msg); + } + Enqueue(Utils.List2String(msgs.Take(10).ToList(), true)); + return true; + } } diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index becabf25..0bd0c588 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -546,24 +546,15 @@ public class MainWindowViewModel : MyReactiveObject NoticeManager.Instance.Enqueue(ResUI.CheckServerSettings); return; } - var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, profileItem); - var msgs = new List([..validatorResult.Errors, ..validatorResult.Warnings]); - if (msgs.Count > 0) + var allResult = await CoreConfigContextBuilder.BuildAll(_config, profileItem); + if (NoticeManager.Instance.NotifyValidatorResult(allResult.CombinedValidatorResult) && !allResult.Success) { - foreach (var msg in msgs) - { - NoticeManager.Instance.SendMessage(msg); - } - NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true)); - if (!validatorResult.Success) - { - return; - } + return; } await Task.Run(async () => { - await LoadCore(context); + await LoadCore(allResult.ResolvedMainContext, allResult.PreSocksResult?.Context); await SysProxyHandler.UpdateSysProxy(_config, false); await Task.Delay(1000); }); @@ -604,9 +595,9 @@ public class MainWindowViewModel : MyReactiveObject RxApp.MainThreadScheduler.Schedule(() => BlReloadEnabled = enabled); } - private async Task LoadCore(CoreConfigContext? context) + private async Task LoadCore(CoreConfigContext? mainContext, CoreConfigContext? preContext) { - await CoreManager.Instance.LoadCore(context); + await CoreManager.Instance.LoadCore(mainContext, preContext); } #endregion core job diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs index 91c745d6..2b0de98d 100644 --- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs @@ -764,18 +764,9 @@ public class ProfilesViewModel : MyReactiveObject } var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, item); - var msgs = new List([..validatorResult.Errors, ..validatorResult.Warnings]); - if (msgs.Count > 0) + if (NoticeManager.Instance.NotifyValidatorResult(validatorResult) && !validatorResult.Success) { - foreach (var msg in msgs) - { - NoticeManager.Instance.SendMessage(msg); - } - NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true)); - if (!validatorResult.Success) - { - return; - } + return; } if (blClipboard) @@ -804,18 +795,9 @@ public class ProfilesViewModel : MyReactiveObject return; } var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, item); - var msgs = new List([..validatorResult.Errors, ..validatorResult.Warnings]); - if (msgs.Count > 0) + if (NoticeManager.Instance.NotifyValidatorResult(validatorResult) && !validatorResult.Success) { - foreach (var msg in msgs) - { - NoticeManager.Instance.SendMessage(msg); - } - NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true)); - if (!validatorResult.Success) - { - return; - } + return; } var result = await CoreConfigHandler.GenerateClientConfig(context, fileName); if (result.Success != true)