From bf8783fed733a56c1aea9e528caa944b5d3db724 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 20 Sep 2025 14:06:41 +0800
Subject: [PATCH 001/132] Update CheckUpdateViewModel.cs
---
.../ViewModels/CheckUpdateViewModel.cs | 25 ++++++-------------
1 file changed, 8 insertions(+), 17 deletions(-)
diff --git a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
index 4c9c0550..5ec1f80b 100644
--- a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
@@ -38,7 +38,7 @@ public class CheckUpdateViewModel : MyReactiveObject
this.WhenAnyValue(
x => x.EnableCheckPreReleaseUpdate,
y => y == true)
- .Subscribe(c => { _config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate; });
+ .Subscribe(c => _config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate);
RefreshCheckUpdateItems();
}
@@ -158,11 +158,8 @@ public class CheckUpdateViewModel : MyReactiveObject
UpdatedPlusPlus(_geo, "");
}
}
- await (new UpdateService()).UpdateGeoFileAll(_config, _updateUI)
- .ContinueWith(t =>
- {
- UpdatedPlusPlus(_geo, "");
- });
+ await new UpdateService().UpdateGeoFileAll(_config, _updateUI)
+ .ContinueWith(t => UpdatedPlusPlus(_geo, ""));
}
private async Task CheckUpdateN(bool preRelease)
@@ -176,11 +173,8 @@ public class CheckUpdateViewModel : MyReactiveObject
UpdatedPlusPlus(_v2rayN, msg);
}
}
- await (new UpdateService()).CheckUpdateGuiN(_config, _updateUI, preRelease)
- .ContinueWith(t =>
- {
- UpdatedPlusPlus(_v2rayN, "");
- });
+ await new UpdateService().CheckUpdateGuiN(_config, _updateUI, preRelease)
+ .ContinueWith(t => UpdatedPlusPlus(_v2rayN, ""));
}
private async Task CheckUpdateCore(CheckUpdateModel model, bool preRelease)
@@ -196,11 +190,8 @@ public class CheckUpdateViewModel : MyReactiveObject
}
}
var type = (ECoreType)Enum.Parse(typeof(ECoreType), model.CoreType);
- await (new UpdateService()).CheckUpdateCore(type, _config, _updateUI, preRelease)
- .ContinueWith(t =>
- {
- UpdatedPlusPlus(model.CoreType, "");
- });
+ await new UpdateService().CheckUpdateCore(type, _config, _updateUI, preRelease)
+ .ContinueWith(t => UpdatedPlusPlus(model.CoreType, ""));
}
private async Task UpdateFinished()
@@ -311,7 +302,7 @@ public class CheckUpdateViewModel : MyReactiveObject
if (Utils.IsNonWindows())
{
- var filesList = (new DirectoryInfo(toPath)).GetFiles().Select(u => u.FullName).ToList();
+ var filesList = new DirectoryInfo(toPath).GetFiles().Select(u => u.FullName).ToList();
foreach (var file in filesList)
{
await Utils.SetLinuxChmod(Path.Combine(toPath, item.CoreType.ToLower()));
From ef30d389dcee3202f03a4a8b7b554ad5e1a8c35b Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 20 Sep 2025 14:06:55 +0800
Subject: [PATCH 002/132] up 7.14.11
---
v2rayN/Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props
index 7bae47f6..ee835a9a 100644
--- a/v2rayN/Directory.Build.props
+++ b/v2rayN/Directory.Build.props
@@ -1,7 +1,7 @@
- 7.14.10
+ 7.14.11
From 3a21596d959f447a57cd344f1a1b7cca7ce447fa Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 21 Sep 2025 12:05:06 +0800
Subject: [PATCH 003/132] Fix node domain resolving in TUN mode
https://github.com/2dust/v2rayN/pull/7989
---
v2rayN/ServiceLib/Global.cs | 1 +
v2rayN/ServiceLib/Handler/ConfigHandler.cs | 4 +-
.../CoreConfig/Singbox/SingboxDnsService.cs | 53 ++++++++++++-------
v2rayN/v2rayN/Common/QRCodeUtils.cs | 4 +-
4 files changed, 38 insertions(+), 24 deletions(-)
diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs
index eb24d71e..b45a6eb3 100644
--- a/v2rayN/ServiceLib/Global.cs
+++ b/v2rayN/ServiceLib/Global.cs
@@ -598,6 +598,7 @@ public class Global
{ "cloudflare-dns.com", new List { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } },
{ "dns.cloudflare.com", new List { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } },
{ "dot.pub", new List { "1.12.12.12", "120.53.53.53" } },
+ { "doh.pub", new List { "1.12.12.12", "120.53.53.53" } },
{ "dns.quad9.net", new List { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } },
{ "dns.yandex.net", new List { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } },
{ "dns.sb", new List { "185.222.222.222", "2a09::" } },
diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
index 786f3aff..65f2ee53 100644
--- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs
+++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
@@ -1214,11 +1214,11 @@ public static class ConfigHandler
CoreType = ECoreType.sing_box,
ConfigType = EConfigType.SOCKS,
Address = Global.Loopback,
- Sni = node.Address, //Tun2SocksAddress
+ SpiderX = node.Address, // Tun2SocksAddress
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
};
}
- else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
+ else if (node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)
{
var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
itemSocks = new ProfileItem()
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
index 00bb14d9..9bd2502d 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
@@ -43,16 +43,7 @@ public partial class CoreConfigSingboxService
});
}
- // Tun2SocksAddress
- if (node != null && Utils.IsDomain(node.Address))
- {
- singboxConfig.dns.rules ??= new List();
- singboxConfig.dns.rules.Insert(0, new Rule4Sbox
- {
- server = Global.SingboxOutboundResolverTag,
- domain = [node.Address],
- });
- }
+ await GenOutboundDnsRule(node, singboxConfig, Global.SingboxOutboundResolverTag);
}
catch (Exception ex)
{
@@ -346,16 +337,7 @@ public partial class CoreConfigSingboxService
await GenDnsDomainsLegacyCompatible(singboxConfig, item);
}
- // Tun2SocksAddress
- if (node != null && Utils.IsDomain(node.Address))
- {
- singboxConfig.dns.rules ??= new List();
- singboxConfig.dns.rules.Insert(0, new Rule4Sbox
- {
- server = Global.SingboxFinalResolverTag,
- domain = [node.Address],
- });
- }
+ await GenOutboundDnsRule(node, singboxConfig, Global.SingboxFinalResolverTag);
}
catch (Exception ex)
{
@@ -425,6 +407,37 @@ public partial class CoreConfigSingboxService
return await Task.FromResult(0);
}
+ private async Task GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig, string? server)
+ {
+ if (node == null)
+ {
+ return 0;
+ }
+
+ var domain = string.Empty;
+ if (Utils.IsDomain(node.Address)) // normal outbound
+ {
+ domain = node.Address;
+ }
+ else if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty() && Utils.IsDomain(node.SpiderX)) // Tun2SocksAddress
+ {
+ domain = node.SpiderX;
+ }
+ if (domain.IsNullOrEmpty())
+ {
+ return 0;
+ }
+
+ singboxConfig.dns.rules ??= new List();
+ singboxConfig.dns.rules.Insert(0, new Rule4Sbox
+ {
+ server = server,
+ domain = [domain],
+ });
+
+ return await Task.FromResult(0);
+ }
+
private static Server4Sbox? ParseDnsAddress(string address)
{
var addressFirst = address?.Split(address.Contains(',') ? ',' : ';').FirstOrDefault()?.Trim();
diff --git a/v2rayN/v2rayN/Common/QRCodeUtils.cs b/v2rayN/v2rayN/Common/QRCodeUtils.cs
index 3013e45f..ef98aca8 100644
--- a/v2rayN/v2rayN/Common/QRCodeUtils.cs
+++ b/v2rayN/v2rayN/Common/QRCodeUtils.cs
@@ -32,8 +32,8 @@ public class QRCodeUtils
{
GetDpi(window, out var dpiX, out var dpiY);
- var left = (int)(SystemParameters.WorkArea.Left);
- var top = (int)(SystemParameters.WorkArea.Top);
+ var left = (int)SystemParameters.WorkArea.Left;
+ var top = (int)SystemParameters.WorkArea.Top;
var width = (int)(SystemParameters.WorkArea.Width / dpiX);
var height = (int)(SystemParameters.WorkArea.Height / dpiY);
From c2c13ad3188014ddc5960c52cc5a8c1feaebb3e6 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 21 Sep 2025 12:12:24 +0800
Subject: [PATCH 004/132] Create v2rayN.slnx
https://github.com/2dust/v2rayN/pull/7969
---
v2rayN/v2rayN.slnx | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 v2rayN/v2rayN.slnx
diff --git a/v2rayN/v2rayN.slnx b/v2rayN/v2rayN.slnx
new file mode 100644
index 00000000..05c0ea96
--- /dev/null
+++ b/v2rayN/v2rayN.slnx
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 534c7ab444b5ec95e0a222f34fbbb69154cb5e24 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 21 Sep 2025 14:35:49 +0800
Subject: [PATCH 005/132] Optimize and improve QR code display
---
v2rayN/ServiceLib/Common/QRCodeUtils.cs | 42 +++++++++++++++++--
v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml | 16 ++++---
.../v2rayN.Desktop/Views/QrcodeView.axaml.cs | 12 +++++-
v2rayN/v2rayN/Common/QRCodeUtils.cs | 3 +-
v2rayN/v2rayN/Views/QrcodeView.xaml | 21 ++++++----
5 files changed, 75 insertions(+), 19 deletions(-)
diff --git a/v2rayN/ServiceLib/Common/QRCodeUtils.cs b/v2rayN/ServiceLib/Common/QRCodeUtils.cs
index 3d3dc90b..d9015367 100644
--- a/v2rayN/ServiceLib/Common/QRCodeUtils.cs
+++ b/v2rayN/ServiceLib/Common/QRCodeUtils.cs
@@ -1,4 +1,5 @@
using QRCoder;
+using QRCoder.Exceptions;
using SkiaSharp;
using ZXing.SkiaSharp;
@@ -8,10 +9,45 @@ public class QRCodeUtils
{
public static byte[]? GenQRCode(string? url)
{
+ if (url.IsNullOrEmpty())
+ {
+ return null;
+ }
using QRCodeGenerator qrGenerator = new();
- using var qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q);
- using PngByteQRCode qrCode = new(qrCodeData);
- return qrCode.GetGraphic(20);
+ DataTooLongException? lastDtle = null;
+
+ var levels = new[]
+ {
+ QRCodeGenerator.ECCLevel.H,
+ QRCodeGenerator.ECCLevel.Q,
+ QRCodeGenerator.ECCLevel.M,
+ QRCodeGenerator.ECCLevel.L
+ };
+ foreach (var level in levels)
+ {
+ try
+ {
+ using var qrCodeData = qrGenerator.CreateQrCode(url, level);
+ using PngByteQRCode qrCode = new(qrCodeData);
+ return qrCode.GetGraphic(20);
+ }
+ catch (DataTooLongException ex)
+ {
+ lastDtle = ex;
+ continue;
+ }
+ catch
+ {
+ throw;
+ }
+ }
+
+ if (lastDtle != null)
+ {
+ throw lastDtle;
+ }
+
+ return null;
}
public static string? ParseBarcode(string? fileName)
diff --git a/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml b/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml
index 90d94685..b67a7a1f 100644
--- a/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml
@@ -4,19 +4,25 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- d:DesignHeight="480"
- d:DesignWidth="400"
+ xmlns:sys="clr-namespace:System;assembly=netstandard"
+ d:DesignHeight="600"
+ d:DesignWidth="600"
mc:Ignorable="d">
+
+
+ 500
+
+
+ Width="{StaticResource QrcodeWidth}"
+ Height="{StaticResource QrcodeWidth}" />
+
+ 500
+
+
-
+
-
\ No newline at end of file
+
From 3e1e23a52472fdb44338f3d7163e7661c57eae4a Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 21 Sep 2025 14:48:54 +0800
Subject: [PATCH 006/132] Update Directory.Packages.props
---
v2rayN/Directory.Packages.props | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props
index 6f8fadf2..71d906f7 100644
--- a/v2rayN/Directory.Packages.props
+++ b/v2rayN/Directory.Packages.props
@@ -18,8 +18,8 @@
-
-
+
+
From 18ac76e683eeeb3011353bee14ddbe4167e338cf Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 21 Sep 2025 14:50:01 +0800
Subject: [PATCH 007/132] up 7.14.12
---
v2rayN/Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props
index ee835a9a..c9f59177 100644
--- a/v2rayN/Directory.Build.props
+++ b/v2rayN/Directory.Build.props
@@ -1,7 +1,7 @@
- 7.14.11
+ 7.14.12
From 27b45aee8312edda910b98460a73a00ecc8e778d Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 23 Sep 2025 11:39:55 +0800
Subject: [PATCH 008/132] Optimization and improvement, using event subscribers
---
v2rayN/ServiceLib/Handler/AppEvents.cs | 4 ++++
.../ServiceLib/ViewModels/MainWindowViewModel.cs | 2 +-
v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs | 14 ++++++++++++--
v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs | 2 +-
v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs | 2 --
v2rayN/v2rayN/Views/ProfilesView.xaml.cs | 2 --
6 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/v2rayN/ServiceLib/Handler/AppEvents.cs b/v2rayN/ServiceLib/Handler/AppEvents.cs
index 109ee762..1cf3204b 100644
--- a/v2rayN/ServiceLib/Handler/AppEvents.cs
+++ b/v2rayN/ServiceLib/Handler/AppEvents.cs
@@ -7,6 +7,8 @@ public static class AppEvents
{
public static readonly Subject ProfilesRefreshRequested = new();
+ public static readonly Subject SubscriptionsRefreshRequested = new();
+
public static readonly Subject SendSnackMsgRequested = new();
public static readonly Subject SendMsgViewRequested = new();
@@ -18,4 +20,6 @@ public static class AppEvents
public static readonly Subject AdjustMainLvColWidthRequested = new();
public static readonly Subject DispatcherStatisticsRequested = new();
+
+ public static readonly Subject SetDefaultServerRequested = new();
}
diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
index 4e9fc9fc..4bc9997d 100644
--- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
@@ -301,7 +301,7 @@ public class MainWindowViewModel : MyReactiveObject
private void RefreshSubscriptions()
{
- Locator.Current.GetService()?.RefreshSubscriptions();
+ AppEvents.SubscriptionsRefreshRequested.OnNext(Unit.Default);
}
#endregion Servers && Groups
diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
index 4c0fd2b9..87e1d78f 100644
--- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
@@ -240,11 +240,21 @@ public class ProfilesViewModel : MyReactiveObject
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await RefreshServersBiz());
+ AppEvents.SubscriptionsRefreshRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(async _ => await RefreshSubscriptions());
+
AppEvents.DispatcherStatisticsRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async result => await UpdateStatistics(result));
+ AppEvents.SetDefaultServerRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(async indexId => await SetDefaultServer(indexId));
+
#endregion AppEvents
_ = Init();
@@ -380,7 +390,7 @@ public class ProfilesViewModel : MyReactiveObject
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
}
- public async Task RefreshSubscriptions()
+ private async Task RefreshSubscriptions()
{
SubItems.Clear();
@@ -565,7 +575,7 @@ public class ProfilesViewModel : MyReactiveObject
await SetDefaultServer(SelectedProfile.IndexId);
}
- public async Task SetDefaultServer(string? indexId)
+ private async Task SetDefaultServer(string? indexId)
{
if (indexId.IsNullOrEmpty())
{
diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
index e9ee033e..e51f9418 100644
--- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
@@ -329,7 +329,7 @@ public class StatusBarViewModel : MyReactiveObject
{
return;
}
- Locator.Current.GetService()?.SetDefaultServer(SelectedServer.ID);
+ AppEvents.SetDefaultServerRequested.OnNext(SelectedServer.ID);
}
public async Task TestServerAvailability()
diff --git a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs
index 5bade399..88763851 100644
--- a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs
@@ -8,7 +8,6 @@ using Avalonia.Threading;
using DialogHostAvalonia;
using MsBox.Avalonia.Enums;
using ReactiveUI;
-using Splat;
using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views;
@@ -48,7 +47,6 @@ public partial class ProfilesView : ReactiveUserControl
//}
ViewModel = new ProfilesViewModel(UpdateViewHandler);
- Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel));
this.WhenActivated(disposables =>
{
diff --git a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs
index 8ef236ab..3c46966c 100644
--- a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs
+++ b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs
@@ -8,7 +8,6 @@ using System.Windows.Media;
using System.Windows.Threading;
using MaterialDesignThemes.Wpf;
using ReactiveUI;
-using Splat;
using v2rayN.Base;
using Point = System.Windows.Point;
@@ -42,7 +41,6 @@ public partial class ProfilesView
}
ViewModel = new ProfilesViewModel(UpdateViewHandler);
- Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel));
this.WhenActivated(disposables =>
{
From 721d70c8c76e3e64bfebeeacc79540cb2d229b28 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 23 Sep 2025 11:39:57 +0800
Subject: [PATCH 009/132] Update Directory.Packages.props
---
v2rayN/Directory.Packages.props | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props
index 71d906f7..283e95a6 100644
--- a/v2rayN/Directory.Packages.props
+++ b/v2rayN/Directory.Packages.props
@@ -20,11 +20,11 @@
-
+
-
\ No newline at end of file
+
From 6929886b3e49ce079917c3e962460d82c2f97dca Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 23 Sep 2025 12:08:43 +0800
Subject: [PATCH 010/132] Optimization and improvement, using event subscribers
---
v2rayN/ServiceLib/Handler/AppEvents.cs | 2 ++
.../ViewModels/ClashProxiesViewModel.cs | 13 +++++++++++
.../ViewModels/MainWindowViewModel.cs | 22 +++++++++----------
.../Views/ClashProxiesView.axaml.cs | 2 --
v2rayN/v2rayN/Views/ClashProxiesView.xaml.cs | 2 --
5 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/v2rayN/ServiceLib/Handler/AppEvents.cs b/v2rayN/ServiceLib/Handler/AppEvents.cs
index 1cf3204b..ccc4e5cb 100644
--- a/v2rayN/ServiceLib/Handler/AppEvents.cs
+++ b/v2rayN/ServiceLib/Handler/AppEvents.cs
@@ -9,6 +9,8 @@ public static class AppEvents
public static readonly Subject SubscriptionsRefreshRequested = new();
+ public static readonly Subject ProxiesReloadRequested = new();
+
public static readonly Subject SendSnackMsgRequested = new();
public static readonly Subject SendMsgViewRequested = new();
diff --git a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs
index 343d49df..bf999d12 100644
--- a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs
@@ -69,6 +69,8 @@ public class ClashProxiesViewModel : MyReactiveObject
SortingSelected = _config.ClashUIItem.ProxiesSorting;
RuleModeSelected = (int)_config.ClashUIItem.RuleMode;
+ #region WhenAnyValue && ReactiveCommand
+
this.WhenAnyValue(
x => x.SelectedGroup,
y => y != null && y.Name.IsNotEmpty())
@@ -89,6 +91,17 @@ public class ClashProxiesViewModel : MyReactiveObject
y => y == true)
.Subscribe(c => { _config.ClashUIItem.ProxiesAutoRefresh = AutoRefresh; });
+ #endregion WhenAnyValue && ReactiveCommand
+
+ #region AppEvents
+
+ AppEvents.ProxiesReloadRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(async _ => await ProxiesReload());
+
+ #endregion AppEvents
+
_ = Init();
}
diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
index 4bc9997d..249a4322 100644
--- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
@@ -520,7 +520,13 @@ public class MainWindowViewModel : MyReactiveObject
});
Locator.Current.GetService()?.TestServerAvailability();
- RxApp.MainThreadScheduler.Schedule(() => _ = ReloadResult());
+ var showClashUI = _config.IsRunningCore(ECoreType.sing_box);
+ if (showClashUI)
+ {
+ AppEvents.ProxiesReloadRequested.OnNext(Unit.Default);
+ }
+
+ RxApp.MainThreadScheduler.Schedule(() => ReloadResult(showClashUI));
BlReloadEnabled = true;
if (_hasNextReloadJob)
@@ -530,19 +536,11 @@ public class MainWindowViewModel : MyReactiveObject
}
}
- public async Task ReloadResult()
+ private void ReloadResult(bool showClashUI)
{
// BlReloadEnabled = true;
- //Locator.Current.GetService()?.ChangeSystemProxyAsync(_config.systemProxyItem.sysProxyType, false);
- ShowClashUI = _config.IsRunningCore(ECoreType.sing_box);
- if (ShowClashUI)
- {
- Locator.Current.GetService()?.ProxiesReload();
- }
- else
- {
- TabMainSelectedIndex = 0;
- }
+ ShowClashUI = showClashUI;
+ TabMainSelectedIndex = showClashUI ? TabMainSelectedIndex : 0;
}
private async Task LoadCore()
diff --git a/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs
index 754dd2a1..c8e7aeba 100644
--- a/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs
@@ -3,7 +3,6 @@ using Avalonia.Input;
using Avalonia.ReactiveUI;
using DynamicData;
using ReactiveUI;
-using Splat;
namespace v2rayN.Desktop.Views;
@@ -13,7 +12,6 @@ public partial class ClashProxiesView : ReactiveUserControl ViewModel, typeof(ClashProxiesViewModel));
lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped;
this.KeyDown += ClashProxiesView_KeyDown;
diff --git a/v2rayN/v2rayN/Views/ClashProxiesView.xaml.cs b/v2rayN/v2rayN/Views/ClashProxiesView.xaml.cs
index eff95de9..23943da1 100644
--- a/v2rayN/v2rayN/Views/ClashProxiesView.xaml.cs
+++ b/v2rayN/v2rayN/Views/ClashProxiesView.xaml.cs
@@ -1,7 +1,6 @@
using System.Reactive.Disposables;
using System.Windows.Input;
using ReactiveUI;
-using Splat;
namespace v2rayN.Views;
@@ -14,7 +13,6 @@ public partial class ClashProxiesView
{
InitializeComponent();
ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
- Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ClashProxiesViewModel));
lstProxyDetails.PreviewMouseDoubleClick += lstProxyDetails_PreviewMouseDoubleClick;
this.WhenActivated(disposables =>
From 0377e7ce19f60200caee10909f5a506623bfa4e8 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 23 Sep 2025 14:27:42 +0800
Subject: [PATCH 011/132] Optimization and improvement, using event subscribers
---
v2rayN/ServiceLib/Handler/AppEvents.cs | 14 +++++-----
.../ViewModels/MainWindowViewModel.cs | 10 +++----
.../ViewModels/StatusBarViewModel.cs | 28 ++++++++++++++++---
.../v2rayN.Desktop/Views/MainWindow.axaml.cs | 2 +-
v2rayN/v2rayN/Views/MainWindow.xaml.cs | 8 ++----
v2rayN/v2rayN/Views/StatusBarView.xaml.cs | 2 --
6 files changed, 38 insertions(+), 26 deletions(-)
diff --git a/v2rayN/ServiceLib/Handler/AppEvents.cs b/v2rayN/ServiceLib/Handler/AppEvents.cs
index ccc4e5cb..a4c2917d 100644
--- a/v2rayN/ServiceLib/Handler/AppEvents.cs
+++ b/v2rayN/ServiceLib/Handler/AppEvents.cs
@@ -6,22 +6,22 @@ namespace ServiceLib.Handler;
public static class AppEvents
{
public static readonly Subject ProfilesRefreshRequested = new();
-
public static readonly Subject SubscriptionsRefreshRequested = new();
-
- public static readonly Subject ProxiesReloadRequested = new();
+ public static readonly Subject ProxiesReloadRequested = new();
+ public static readonly Subject DispatcherStatisticsRequested = new();
public static readonly Subject SendSnackMsgRequested = new();
-
public static readonly Subject SendMsgViewRequested = new();
public static readonly Subject AppExitRequested = new();
-
public static readonly Subject ShutdownRequested = new();
public static readonly Subject AdjustMainLvColWidthRequested = new();
- public static readonly Subject DispatcherStatisticsRequested = new();
-
public static readonly Subject SetDefaultServerRequested = new();
+
+ public static readonly Subject RoutingsMenuRefreshRequested = new();
+ public static readonly Subject TestServerRequested = new();
+ public static readonly Subject InboundDisplayRequested = new();
+ public static readonly Subject SysProxyChangeRequested = new();
}
diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
index 249a4322..3492670c 100644
--- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
@@ -2,7 +2,6 @@ using System.Reactive;
using System.Reactive.Concurrency;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
-using Splat;
namespace ServiceLib.ViewModels;
@@ -240,7 +239,6 @@ public class MainWindowViewModel : MyReactiveObject
BlReloadEnabled = true;
await Reload();
await AutoHideStartup();
- Locator.Current.GetService()?.RefreshRoutingsMenu();
}
#endregion Init
@@ -433,7 +431,7 @@ public class MainWindowViewModel : MyReactiveObject
var ret = await _updateView?.Invoke(EViewAction.OptionSettingWindow, null);
if (ret == true)
{
- Locator.Current.GetService()?.InboundDisplayStatus();
+ AppEvents.InboundDisplayRequested.OnNext(Unit.Default);
await Reload();
}
}
@@ -444,7 +442,7 @@ public class MainWindowViewModel : MyReactiveObject
if (ret == true)
{
await ConfigHandler.InitBuiltinRouting(_config);
- Locator.Current.GetService()?.RefreshRoutingsMenu();
+ AppEvents.RoutingsMenuRefreshRequested.OnNext(Unit.Default);
await Reload();
}
}
@@ -518,7 +516,7 @@ public class MainWindowViewModel : MyReactiveObject
await SysProxyHandler.UpdateSysProxy(_config, false);
await Task.Delay(1000);
});
- Locator.Current.GetService()?.TestServerAvailability();
+ AppEvents.TestServerRequested.OnNext(Unit.Default);
var showClashUI = _config.IsRunningCore(ECoreType.sing_box);
if (showClashUI)
@@ -572,7 +570,7 @@ public class MainWindowViewModel : MyReactiveObject
{
await ConfigHandler.ApplyRegionalPreset(_config, type);
await ConfigHandler.InitRouting(_config);
- Locator.Current.GetService()?.RefreshRoutingsMenu();
+ AppEvents.RoutingsMenuRefreshRequested.OnNext(Unit.Default);
await ConfigHandler.SaveConfig(_config);
await new UpdateService().UpdateGeoFileAll(_config, UpdateTaskHandler);
diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
index e51f9418..9465e6a2 100644
--- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
@@ -209,6 +209,26 @@ public class StatusBarViewModel : MyReactiveObject
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async result => await UpdateStatistics(result));
+ AppEvents.RoutingsMenuRefreshRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(async _ => await RefreshRoutingsMenu());
+
+ AppEvents.TestServerRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(async _ => await TestServerAvailability());
+
+ AppEvents.InboundDisplayRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(async _ => await InboundDisplayStatus());
+
+ AppEvents.SysProxyChangeRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(async result => await SetListenerType(result));
+
#endregion AppEvents
_ = Init();
@@ -329,7 +349,7 @@ public class StatusBarViewModel : MyReactiveObject
{
return;
}
- AppEvents.SetDefaultServerRequested.OnNext(SelectedServer.ID);
+ AppEvents.SetDefaultServerRequested.OnNext(SelectedServer.ID);
}
public async Task TestServerAvailability()
@@ -364,7 +384,7 @@ public class StatusBarViewModel : MyReactiveObject
#region System proxy and Routings
- public async Task SetListenerType(ESysProxyType type)
+ private async Task SetListenerType(ESysProxyType type)
{
if (_config.SystemProxyItem.SysProxyType == type)
{
@@ -393,7 +413,7 @@ public class StatusBarViewModel : MyReactiveObject
}
}
- public async Task RefreshRoutingsMenu()
+ private async Task RefreshRoutingsMenu()
{
RoutingItems.Clear();
@@ -501,7 +521,7 @@ public class StatusBarViewModel : MyReactiveObject
#region UI
- public async Task InboundDisplayStatus()
+ private async Task InboundDisplayStatus()
{
StringBuilder sb = new();
sb.Append($"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}");
diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
index d72c37a7..532bda6f 100644
--- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
@@ -259,7 +259,7 @@ public partial class MainWindow : WindowBase
case EGlobalHotkey.SystemProxySet:
case EGlobalHotkey.SystemProxyUnchanged:
case EGlobalHotkey.SystemProxyPac:
- Locator.Current.GetService()?.SetListenerType((ESysProxyType)((int)e - 1));
+ AppEvents.SysProxyChangeRequested.OnNext((ESysProxyType)((int)e - 1));
break;
}
}
diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs
index a804775e..28073be8 100644
--- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs
@@ -249,7 +249,7 @@ public partial class MainWindow
case EGlobalHotkey.SystemProxySet:
case EGlobalHotkey.SystemProxyUnchanged:
case EGlobalHotkey.SystemProxyPac:
- Locator.Current.GetService()?.SetListenerType((ESysProxyType)((int)e - 1));
+ AppEvents.SysProxyChangeRequested.OnNext((ESysProxyType)((int)e - 1));
break;
}
}
@@ -287,11 +287,7 @@ public partial class MainWindow
var clipboardData = WindowsUtils.GetClipboardData();
if (clipboardData.IsNotEmpty())
{
- var service = Locator.Current.GetService();
- if (service != null)
- {
- _ = service.AddServerViaClipboardAsync(clipboardData);
- }
+ ViewModel?.AddServerViaClipboardAsync(clipboardData);
}
break;
diff --git a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs
index 8a3a5df4..f9b551c2 100644
--- a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs
+++ b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs
@@ -3,7 +3,6 @@ using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using ReactiveUI;
-using Splat;
using v2rayN.Manager;
namespace v2rayN.Views;
@@ -17,7 +16,6 @@ public partial class StatusBarView
InitializeComponent();
_config = AppManager.Instance.Config;
ViewModel = new StatusBarViewModel(UpdateViewHandler);
- Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
menuExit.Click += menuExit_Click;
txtRunningServerDisplay.PreviewMouseDown += txtRunningInfoDisplay_MouseDoubleClick;
From e96a4818c47a2d7b707ba7570720c8f7ef67c9df Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 23 Sep 2025 15:31:19 +0800
Subject: [PATCH 012/132] Optimization and improvement
---
v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs | 3 +++
v2rayN/v2rayN.Desktop/App.axaml.cs | 4 +---
v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs | 2 +-
v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs | 6 ++----
v2rayN/v2rayN/Views/StatusBarView.xaml.cs | 3 ++-
5 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
index 9465e6a2..ee66351c 100644
--- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
@@ -11,6 +11,9 @@ namespace ServiceLib.ViewModels;
public class StatusBarViewModel : MyReactiveObject
{
+ private static readonly Lazy _instance = new(() => new(null));
+ public static StatusBarViewModel Instance => _instance.Value;
+
#region ObservableCollection
public IObservableCollection RoutingItems { get; } = new ObservableCollectionExtended();
diff --git a/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayN/v2rayN.Desktop/App.axaml.cs
index aa357ca4..515354d5 100644
--- a/v2rayN/v2rayN.Desktop/App.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/App.axaml.cs
@@ -16,9 +16,7 @@ public partial class App : Application
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
- var ViewModel = new StatusBarViewModel(null);
- Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
- DataContext = ViewModel;
+ DataContext = StatusBarViewModel.Instance;
}
public override void OnFrameworkInitializationCompleted()
diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
index 532bda6f..eb884e4b 100644
--- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
@@ -20,7 +20,7 @@ namespace v2rayN.Desktop.Views;
public partial class MainWindow : WindowBase
{
private static Config _config;
- private WindowNotificationManager? _manager;
+ private readonly WindowNotificationManager? _manager;
private CheckUpdateView? _checkUpdateView;
private BackupAndRestoreView? _backupAndRestoreView;
private bool _blCloseByUser = false;
diff --git a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs
index 3eb08f96..976c156d 100644
--- a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs
@@ -6,7 +6,6 @@ using Avalonia.ReactiveUI;
using Avalonia.Threading;
using DialogHostAvalonia;
using ReactiveUI;
-using Splat;
using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views;
@@ -20,9 +19,8 @@ public partial class StatusBarView : ReactiveUserControl
InitializeComponent();
_config = AppManager.Instance.Config;
- //ViewModel = new StatusBarViewModel(UpdateViewHandler);
- //Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
- ViewModel = Locator.Current.GetService();
+
+ ViewModel = StatusBarViewModel.Instance;
ViewModel?.InitUpdateView(UpdateViewHandler);
txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped;
diff --git a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs
index f9b551c2..1d80a694 100644
--- a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs
+++ b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs
@@ -15,7 +15,8 @@ public partial class StatusBarView
{
InitializeComponent();
_config = AppManager.Instance.Config;
- ViewModel = new StatusBarViewModel(UpdateViewHandler);
+ ViewModel = StatusBarViewModel.Instance;
+ ViewModel?.InitUpdateView(UpdateViewHandler);
menuExit.Click += menuExit_Click;
txtRunningServerDisplay.PreviewMouseDown += txtRunningInfoDisplay_MouseDoubleClick;
From 671678724b3cc2186f9d2370350bbb80dcdb0b6c Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 24 Sep 2025 10:57:06 +0800
Subject: [PATCH 013/132] Optimization and improvement, using event subscribers
---
v2rayN/ServiceLib/Enums/EViewAction.cs | 1 -
v2rayN/ServiceLib/Handler/AppEvents.cs | 6 +++
v2rayN/ServiceLib/Manager/AppManager.cs | 6 +++
.../ViewModels/CheckUpdateViewModel.cs | 6 +--
.../ViewModels/MainWindowViewModel.cs | 46 +++++++++++--------
.../ViewModels/ProfilesViewModel.cs | 3 +-
.../ViewModels/StatusBarViewModel.cs | 28 +++++------
v2rayN/v2rayN.Desktop/App.axaml.cs | 15 ++----
.../v2rayN.Desktop/Views/MainWindow.axaml.cs | 35 +++++++-------
v2rayN/v2rayN/Views/MainWindow.xaml.cs | 46 +++++++++----------
10 files changed, 95 insertions(+), 97 deletions(-)
diff --git a/v2rayN/ServiceLib/Enums/EViewAction.cs b/v2rayN/ServiceLib/Enums/EViewAction.cs
index 2c47d31d..075439fd 100644
--- a/v2rayN/ServiceLib/Enums/EViewAction.cs
+++ b/v2rayN/ServiceLib/Enums/EViewAction.cs
@@ -12,7 +12,6 @@ public enum EViewAction
ProfilesFocus,
ShareSub,
ShareServer,
- ShowHideWindow,
ScanScreenTask,
ScanImageTask,
BrowseServer,
diff --git a/v2rayN/ServiceLib/Handler/AppEvents.cs b/v2rayN/ServiceLib/Handler/AppEvents.cs
index a4c2917d..bda78922 100644
--- a/v2rayN/ServiceLib/Handler/AppEvents.cs
+++ b/v2rayN/ServiceLib/Handler/AppEvents.cs
@@ -5,6 +5,12 @@ namespace ServiceLib.Handler;
public static class AppEvents
{
+ public static readonly Subject ReloadRequested = new();
+ public static readonly Subject ShowHideWindowRequested = new();
+ public static readonly Subject AddServerViaScanRequested = new();
+ public static readonly Subject AddServerViaClipboardRequested = new();
+ public static readonly Subject SubscriptionsUpdateRequested = new();
+
public static readonly Subject ProfilesRefreshRequested = new();
public static readonly Subject SubscriptionsRefreshRequested = new();
public static readonly Subject ProxiesReloadRequested = new();
diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs
index 33125289..57238b15 100644
--- a/v2rayN/ServiceLib/Manager/AppManager.cs
+++ b/v2rayN/ServiceLib/Manager/AppManager.cs
@@ -122,6 +122,12 @@ public sealed class AppManager
AppEvents.ShutdownRequested.OnNext(byUser);
}
+ public async Task RebootAsAdmin()
+ {
+ ProcUtils.RebootAsAdmin();
+ await AppManager.Instance.AppExitAsync(true);
+ }
+
#endregion App
#region Config
diff --git a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
index 5ec1f80b..171cb467 100644
--- a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
@@ -2,11 +2,9 @@ using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
-using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
-using Splat;
namespace ServiceLib.ViewModels;
@@ -225,11 +223,11 @@ public class CheckUpdateViewModel : MyReactiveObject
{
if (blReload)
{
- Locator.Current.GetService()?.Reload();
+ AppEvents.ReloadRequested.OnNext(Unit.Default);
}
else
{
- Locator.Current.GetService()?.CloseCore();
+ await CoreManager.Instance.CoreStop();
}
}
diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
index 3492670c..ba5de790 100644
--- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
@@ -1,5 +1,6 @@
using System.Reactive;
using System.Reactive.Concurrency;
+using System.Reactive.Linq;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
@@ -183,7 +184,7 @@ public class MainWindowViewModel : MyReactiveObject
});
RebootAsAdminCmd = ReactiveCommand.CreateFromTask(async () =>
{
- await RebootAsAdmin();
+ await AppManager.Instance.RebootAsAdmin();
});
ClearServerStatisticsCmd = ReactiveCommand.CreateFromTask(async () =>
{
@@ -216,6 +217,30 @@ public class MainWindowViewModel : MyReactiveObject
#endregion WhenAnyValue && ReactiveCommand
+ #region AppEvents
+
+ AppEvents.ReloadRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(async _ => await Reload());
+
+ AppEvents.AddServerViaScanRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(async _ => await AddServerViaScanAsync());
+
+ AppEvents.AddServerViaClipboardRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(async _ => await AddServerViaClipboardAsync(null));
+
+ AppEvents.SubscriptionsUpdateRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(async blProxy => await UpdateSubscriptionProcess("", blProxy));
+
+ #endregion AppEvents
+
_ = Init();
}
@@ -281,11 +306,6 @@ public class MainWindowViewModel : MyReactiveObject
AppEvents.DispatcherStatisticsRequested.OnNext(update);
}
- public void ShowHideWindow(bool? blShow)
- {
- _updateView?.Invoke(EViewAction.ShowHideWindow, blShow);
- }
-
#endregion Actions
#region Servers && Groups
@@ -465,12 +485,6 @@ public class MainWindowViewModel : MyReactiveObject
}
}
- public async Task RebootAsAdmin()
- {
- ProcUtils.RebootAsAdmin();
- await AppManager.Instance.AppExitAsync(true);
- }
-
private async Task ClearServerStatistics()
{
await StatisticsManager.Instance.ClearAllServerStatistics();
@@ -547,17 +561,11 @@ public class MainWindowViewModel : MyReactiveObject
await CoreManager.Instance.LoadCore(node);
}
- public async Task CloseCore()
- {
- await ConfigHandler.SaveConfig(_config);
- await CoreManager.Instance.CoreStop();
- }
-
private async Task AutoHideStartup()
{
if (_config.UiItem.AutoHideStartup)
{
- ShowHideWindow(false);
+ AppEvents.ShowHideWindowRequested.OnNext(false);
}
await Task.CompletedTask;
}
diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
index 87e1d78f..fcf3450d 100644
--- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
@@ -6,7 +6,6 @@ using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
-using Splat;
namespace ServiceLib.ViewModels;
@@ -276,7 +275,7 @@ public class ProfilesViewModel : MyReactiveObject
private void Reload()
{
- Locator.Current.GetService()?.Reload();
+ AppEvents.ReloadRequested.OnNext(Unit.Default);
}
public async Task SetSpeedTestResult(SpeedTestResult result)
diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
index ee66351c..6dc795ef 100644
--- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
@@ -5,7 +5,6 @@ using System.Text;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
-using Splat;
namespace ServiceLib.ViewModels;
@@ -149,17 +148,17 @@ public class StatusBarViewModel : MyReactiveObject
NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () =>
{
- Locator.Current.GetService()?.ShowHideWindow(null);
+ AppEvents.ShowHideWindowRequested.OnNext(null);
await Task.CompletedTask;
});
ShowWindowCmd = ReactiveCommand.CreateFromTask(async () =>
{
- Locator.Current.GetService()?.ShowHideWindow(true);
+ AppEvents.ShowHideWindowRequested.OnNext(true);
await Task.CompletedTask;
});
HideWindowCmd = ReactiveCommand.CreateFromTask(async () =>
{
- Locator.Current.GetService()?.ShowHideWindow(false);
+ AppEvents.ShowHideWindowRequested.OnNext(false);
await Task.CompletedTask;
});
@@ -275,23 +274,20 @@ public class StatusBarViewModel : MyReactiveObject
private async Task AddServerViaClipboard()
{
- var service = Locator.Current.GetService();
- if (service != null)
- await service.AddServerViaClipboardAsync(null);
+ AppEvents.AddServerViaClipboardRequested.OnNext(Unit.Default);
+ await Task.Delay(1000);
}
private async Task AddServerViaScan()
{
- var service = Locator.Current.GetService();
- if (service != null)
- await service.AddServerViaScanAsync();
+ AppEvents.AddServerViaScanRequested.OnNext(Unit.Default);
+ await Task.Delay(1000);
}
private async Task UpdateSubscriptionProcess(bool blProxy)
{
- var service = Locator.Current.GetService();
- if (service != null)
- await service.UpdateSubscriptionProcess("", blProxy);
+ AppEvents.SubscriptionsUpdateRequested.OnNext(blProxy);
+ await Task.Delay(1000);
}
private async Task RefreshServersBiz()
@@ -453,7 +449,7 @@ public class StatusBarViewModel : MyReactiveObject
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
{
NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting);
- Locator.Current.GetService()?.Reload();
+ AppEvents.ReloadRequested.OnNext(Unit.Default);
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
}
}
@@ -486,7 +482,7 @@ public class StatusBarViewModel : MyReactiveObject
if (Utils.IsWindows())
{
_config.TunModeItem.EnableTun = false;
- Locator.Current.GetService()?.RebootAsAdmin();
+ await AppManager.Instance.RebootAsAdmin();
return;
}
else
@@ -500,7 +496,7 @@ public class StatusBarViewModel : MyReactiveObject
}
}
await ConfigHandler.SaveConfig(_config);
- Locator.Current.GetService()?.Reload();
+ AppEvents.ReloadRequested.OnNext(Unit.Default);
}
private bool AllowEnableTun()
diff --git a/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayN/v2rayN.Desktop/App.axaml.cs
index 515354d5..fa4779cf 100644
--- a/v2rayN/v2rayN.Desktop/App.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/App.axaml.cs
@@ -1,8 +1,7 @@
+using System.Reactive;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
-using Splat;
-using v2rayN.Desktop.Common;
using v2rayN.Desktop.Views;
namespace v2rayN.Desktop;
@@ -55,16 +54,8 @@ public partial class App : Application
{
if (desktop.MainWindow != null)
{
- var clipboardData = await AvaUtils.GetClipboardData(desktop.MainWindow);
- if (clipboardData.IsNullOrEmpty())
- {
- return;
- }
- var service = Locator.Current.GetService();
- if (service != null)
- {
- _ = service.AddServerViaClipboardAsync(clipboardData);
- }
+ AppEvents.AddServerViaClipboardRequested.OnNext(Unit.Default);
+ await Task.Delay(1000);
}
}
}
diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
index eb884e4b..86bd5d7b 100644
--- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
@@ -10,7 +10,6 @@ using Avalonia.Threading;
using DialogHostAvalonia;
using MsBox.Avalonia.Enums;
using ReactiveUI;
-using Splat;
using v2rayN.Desktop.Base;
using v2rayN.Desktop.Common;
using v2rayN.Desktop.Manager;
@@ -40,7 +39,6 @@ public partial class MainWindow : WindowBase
menuClose.Click += MenuClose_Click;
ViewModel = new MainWindowViewModel(UpdateViewHandler);
- Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
switch (_config.UiItem.MainGirdOrientation)
{
@@ -153,6 +151,12 @@ public partial class MainWindow : WindowBase
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(content => Shutdown(content))
.DisposeWith(disposables);
+
+ AppEvents.ShowHideWindowRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(blShow => ShowHideWindow(blShow))
+ .DisposeWith(disposables);
});
if (Utils.IsWindows())
@@ -221,12 +225,6 @@ public partial class MainWindow : WindowBase
case EViewAction.SubSettingWindow:
return await new SubSettingWindow().ShowDialog(this);
- case EViewAction.ShowHideWindow:
- Dispatcher.UIThread.Post(() =>
- ShowHideWindow((bool?)obj),
- DispatcherPriority.Default);
- break;
-
case EViewAction.ScanScreenTask:
await ScanScreenTaskAsync();
break;
@@ -236,11 +234,7 @@ public partial class MainWindow : WindowBase
break;
case EViewAction.AddServerViaClipboard:
- var clipboardData = await AvaUtils.GetClipboardData(this);
- if (clipboardData.IsNotEmpty() && ViewModel != null)
- {
- await ViewModel.AddServerViaClipboardAsync(clipboardData);
- }
+ await AddServerViaClipboardAsync();
break;
}
@@ -295,11 +289,7 @@ public partial class MainWindow : WindowBase
switch (e.Key)
{
case Key.V:
- var clipboardData = await AvaUtils.GetClipboardData(this);
- if (clipboardData.IsNotEmpty() && ViewModel != null)
- {
- await ViewModel.AddServerViaClipboardAsync(clipboardData);
- }
+ await AddServerViaClipboardAsync();
break;
case Key.S:
@@ -326,6 +316,15 @@ public partial class MainWindow : WindowBase
ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
}
+ public async Task AddServerViaClipboardAsync()
+ {
+ var clipboardData = await AvaUtils.GetClipboardData(this);
+ if (clipboardData.IsNotEmpty() && ViewModel != null)
+ {
+ await ViewModel.AddServerViaClipboardAsync(clipboardData);
+ }
+ }
+
public async Task ScanScreenTaskAsync()
{
//ShowHideWindow(false);
diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs
index 28073be8..1d0d530c 100644
--- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs
@@ -6,10 +6,8 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
-using System.Windows.Threading;
using MaterialDesignThemes.Wpf;
using ReactiveUI;
-using Splat;
using v2rayN.Manager;
namespace v2rayN.Views;
@@ -37,7 +35,6 @@ public partial class MainWindow
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
ViewModel = new MainWindowViewModel(UpdateViewHandler);
- Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
switch (_config.UiItem.MainGirdOrientation)
{
@@ -146,10 +143,16 @@ public partial class MainWindow
.DisposeWith(disposables);
AppEvents.ShutdownRequested
- .AsObservable()
- .ObserveOn(RxApp.MainThreadScheduler)
- .Subscribe(content => Shutdown(content))
- .DisposeWith(disposables);
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(content => Shutdown(content))
+ .DisposeWith(disposables);
+
+ AppEvents.ShowHideWindowRequested
+ .AsObservable()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(blShow => ShowHideWindow(blShow))
+ .DisposeWith(disposables);
});
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
@@ -210,13 +213,6 @@ public partial class MainWindow
case EViewAction.SubSettingWindow:
return (new SubSettingWindow().ShowDialog() ?? false);
- case EViewAction.ShowHideWindow:
- Application.Current?.Dispatcher.Invoke((() =>
- {
- ShowHideWindow((bool?)obj);
- }), DispatcherPriority.Normal);
- break;
-
case EViewAction.ScanScreenTask:
await ScanScreenTaskAsync();
break;
@@ -226,11 +222,7 @@ public partial class MainWindow
break;
case EViewAction.AddServerViaClipboard:
- var clipboardData = WindowsUtils.GetClipboardData();
- if (clipboardData.IsNotEmpty())
- {
- ViewModel?.AddServerViaClipboardAsync(clipboardData);
- }
+ await AddServerViaClipboardAsync();
break;
}
@@ -283,12 +275,7 @@ public partial class MainWindow
{
return;
}
-
- var clipboardData = WindowsUtils.GetClipboardData();
- if (clipboardData.IsNotEmpty())
- {
- ViewModel?.AddServerViaClipboardAsync(clipboardData);
- }
+ AddServerViaClipboardAsync().ContinueWith(_ => { });
break;
@@ -322,6 +309,15 @@ public partial class MainWindow
ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
}
+ public async Task AddServerViaClipboardAsync()
+ {
+ var clipboardData = WindowsUtils.GetClipboardData();
+ if (clipboardData.IsNotEmpty() && ViewModel != null)
+ {
+ await ViewModel.AddServerViaClipboardAsync(clipboardData);
+ }
+ }
+
private async Task ScanScreenTaskAsync()
{
ShowHideWindow(false);
From 6b85aa0b03ce73e97352d882fde5f979f22c98a0 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 24 Sep 2025 10:57:23 +0800
Subject: [PATCH 014/132] Remove Splat.NLog package
---
v2rayN/Directory.Packages.props | 2 +-
v2rayN/ServiceLib/ServiceLib.csproj | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props
index 283e95a6..24484112 100644
--- a/v2rayN/Directory.Packages.props
+++ b/v2rayN/Directory.Packages.props
@@ -20,7 +20,7 @@
-
+
diff --git a/v2rayN/ServiceLib/ServiceLib.csproj b/v2rayN/ServiceLib/ServiceLib.csproj
index 7ee72196..457b065d 100644
--- a/v2rayN/ServiceLib/ServiceLib.csproj
+++ b/v2rayN/ServiceLib/ServiceLib.csproj
@@ -11,7 +11,7 @@
-
+
From faff8e4ea2d32c5381ee122b4296305040ec6e40 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 24 Sep 2025 18:41:00 +0800
Subject: [PATCH 015/132] Remove secret data from mihomo configuration
---
v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs
index e102f17d..b9fcc126 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs
@@ -79,6 +79,7 @@ public class CoreConfigClashService
//external-controller
fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}";
+ fileContent.Remove("secret");
//allow-lan
if (_config.Inbound.First().AllowLANConn)
{
From d86003df55cff21199eb251de3bd2da984ec899b Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Thu, 25 Sep 2025 10:56:10 +0800
Subject: [PATCH 016/132] Optimize and improve the Subject
---
v2rayN/ServiceLib/Events/AppEvents.cs | 32 ++++++++++++++++++
v2rayN/ServiceLib/Events/EventChannel.cs | 29 ++++++++++++++++
v2rayN/ServiceLib/GlobalUsings.cs | 1 +
v2rayN/ServiceLib/Handler/AppEvents.cs | 33 -------------------
v2rayN/ServiceLib/Manager/AppManager.cs | 4 +--
v2rayN/ServiceLib/Manager/NoticeManager.cs | 4 +--
.../ViewModels/CheckUpdateViewModel.cs | 2 +-
.../ViewModels/MainWindowViewModel.cs | 20 +++++------
.../ViewModels/ProfilesViewModel.cs | 4 +--
.../ViewModels/StatusBarViewModel.cs | 18 +++++-----
v2rayN/v2rayN.Desktop/App.axaml.cs | 2 +-
v2rayN/v2rayN.Desktop/GlobalUsings.cs | 1 +
.../v2rayN.Desktop/Views/MainWindow.axaml.cs | 2 +-
v2rayN/v2rayN/GlobalUsings.cs | 1 +
v2rayN/v2rayN/Views/MainWindow.xaml.cs | 2 +-
15 files changed, 93 insertions(+), 62 deletions(-)
create mode 100644 v2rayN/ServiceLib/Events/AppEvents.cs
create mode 100644 v2rayN/ServiceLib/Events/EventChannel.cs
delete mode 100644 v2rayN/ServiceLib/Handler/AppEvents.cs
diff --git a/v2rayN/ServiceLib/Events/AppEvents.cs b/v2rayN/ServiceLib/Events/AppEvents.cs
new file mode 100644
index 00000000..45de4de1
--- /dev/null
+++ b/v2rayN/ServiceLib/Events/AppEvents.cs
@@ -0,0 +1,32 @@
+using System.Reactive;
+
+namespace ServiceLib.Events;
+
+public static class AppEvents
+{
+ public static readonly EventChannel ReloadRequested = new();
+ public static readonly EventChannel ShowHideWindowRequested = new();
+ public static readonly EventChannel AddServerViaScanRequested = new();
+ public static readonly EventChannel AddServerViaClipboardRequested = new();
+ public static readonly EventChannel SubscriptionsUpdateRequested = new();
+
+ public static readonly EventChannel ProfilesRefreshRequested = new();
+ public static readonly EventChannel SubscriptionsRefreshRequested = new();
+ public static readonly EventChannel ProxiesReloadRequested = new();
+ public static readonly EventChannel DispatcherStatisticsRequested = new();
+
+ public static readonly EventChannel SendSnackMsgRequested = new();
+ public static readonly EventChannel SendMsgViewRequested = new();
+
+ public static readonly EventChannel AppExitRequested = new();
+ public static readonly EventChannel ShutdownRequested = new();
+
+ public static readonly EventChannel AdjustMainLvColWidthRequested = new();
+
+ public static readonly EventChannel SetDefaultServerRequested = new();
+
+ public static readonly EventChannel RoutingsMenuRefreshRequested = new();
+ public static readonly EventChannel TestServerRequested = new();
+ public static readonly EventChannel InboundDisplayRequested = new();
+ public static readonly EventChannel SysProxyChangeRequested = new();
+}
diff --git a/v2rayN/ServiceLib/Events/EventChannel.cs b/v2rayN/ServiceLib/Events/EventChannel.cs
new file mode 100644
index 00000000..c3c58f3e
--- /dev/null
+++ b/v2rayN/ServiceLib/Events/EventChannel.cs
@@ -0,0 +1,29 @@
+using System.Reactive;
+using System.Reactive.Linq;
+using System.Reactive.Subjects;
+
+namespace ServiceLib.Events;
+
+public sealed class EventChannel
+{
+ private readonly ISubject _subject = Subject.Synchronize(new Subject());
+
+ public IObservable AsObservable()
+ {
+ return _subject.AsObservable();
+ }
+
+ public void Publish(T value)
+ {
+ _subject.OnNext(value);
+ }
+
+ public void Publish()
+ {
+ if (typeof(T) != typeof(Unit))
+ {
+ throw new InvalidOperationException("Publish() without value is only valid for EventChannel.");
+ }
+ _subject.OnNext((T)(object)Unit.Default);
+ }
+}
diff --git a/v2rayN/ServiceLib/GlobalUsings.cs b/v2rayN/ServiceLib/GlobalUsings.cs
index 9a78c73b..a952f4a8 100644
--- a/v2rayN/ServiceLib/GlobalUsings.cs
+++ b/v2rayN/ServiceLib/GlobalUsings.cs
@@ -1,6 +1,7 @@
global using ServiceLib.Base;
global using ServiceLib.Common;
global using ServiceLib.Enums;
+global using ServiceLib.Events;
global using ServiceLib.Handler;
global using ServiceLib.Helper;
global using ServiceLib.Manager;
diff --git a/v2rayN/ServiceLib/Handler/AppEvents.cs b/v2rayN/ServiceLib/Handler/AppEvents.cs
deleted file mode 100644
index bda78922..00000000
--- a/v2rayN/ServiceLib/Handler/AppEvents.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Reactive;
-using System.Reactive.Subjects;
-
-namespace ServiceLib.Handler;
-
-public static class AppEvents
-{
- public static readonly Subject ReloadRequested = new();
- public static readonly Subject ShowHideWindowRequested = new();
- public static readonly Subject AddServerViaScanRequested = new();
- public static readonly Subject AddServerViaClipboardRequested = new();
- public static readonly Subject SubscriptionsUpdateRequested = new();
-
- public static readonly Subject ProfilesRefreshRequested = new();
- public static readonly Subject SubscriptionsRefreshRequested = new();
- public static readonly Subject ProxiesReloadRequested = new();
- public static readonly Subject DispatcherStatisticsRequested = new();
-
- public static readonly Subject SendSnackMsgRequested = new();
- public static readonly Subject SendMsgViewRequested = new();
-
- public static readonly Subject AppExitRequested = new();
- public static readonly Subject ShutdownRequested = new();
-
- public static readonly Subject AdjustMainLvColWidthRequested = new();
-
- public static readonly Subject SetDefaultServerRequested = new();
-
- public static readonly Subject RoutingsMenuRefreshRequested = new();
- public static readonly Subject TestServerRequested = new();
- public static readonly Subject InboundDisplayRequested = new();
- public static readonly Subject SysProxyChangeRequested = new();
-}
diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs
index 57238b15..a0518d21 100644
--- a/v2rayN/ServiceLib/Manager/AppManager.cs
+++ b/v2rayN/ServiceLib/Manager/AppManager.cs
@@ -96,7 +96,7 @@ public sealed class AppManager
Logging.SaveLog("AppExitAsync Begin");
await SysProxyHandler.UpdateSysProxy(_config, true);
- AppEvents.AppExitRequested.OnNext(Unit.Default);
+ AppEvents.AppExitRequested.Publish();
await Task.Delay(50); //Wait for AppExitRequested to be processed
await ConfigHandler.SaveConfig(_config);
@@ -119,7 +119,7 @@ public sealed class AppManager
public void Shutdown(bool byUser)
{
- AppEvents.ShutdownRequested.OnNext(byUser);
+ AppEvents.ShutdownRequested.Publish(byUser);
}
public async Task RebootAsAdmin()
diff --git a/v2rayN/ServiceLib/Manager/NoticeManager.cs b/v2rayN/ServiceLib/Manager/NoticeManager.cs
index f9e149ed..da034f02 100644
--- a/v2rayN/ServiceLib/Manager/NoticeManager.cs
+++ b/v2rayN/ServiceLib/Manager/NoticeManager.cs
@@ -11,7 +11,7 @@ public class NoticeManager
{
return;
}
- AppEvents.SendSnackMsgRequested.OnNext(content);
+ AppEvents.SendSnackMsgRequested.Publish(content);
}
public void SendMessage(string? content)
@@ -20,7 +20,7 @@ public class NoticeManager
{
return;
}
- AppEvents.SendMsgViewRequested.OnNext(content);
+ AppEvents.SendMsgViewRequested.Publish(content);
}
public void SendMessageEx(string? content)
diff --git a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
index 171cb467..ade01d3c 100644
--- a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
@@ -223,7 +223,7 @@ public class CheckUpdateViewModel : MyReactiveObject
{
if (blReload)
{
- AppEvents.ReloadRequested.OnNext(Unit.Default);
+ AppEvents.ReloadRequested.Publish();
}
else
{
diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
index ba5de790..be8898ae 100644
--- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
@@ -292,7 +292,7 @@ public class MainWindowViewModel : MyReactiveObject
}
if (_config.UiItem.EnableAutoAdjustMainLvColWidth)
{
- AppEvents.AdjustMainLvColWidthRequested.OnNext(Unit.Default);
+ AppEvents.AdjustMainLvColWidthRequested.Publish();
}
}
}
@@ -303,7 +303,7 @@ public class MainWindowViewModel : MyReactiveObject
{
return;
}
- AppEvents.DispatcherStatisticsRequested.OnNext(update);
+ AppEvents.DispatcherStatisticsRequested.Publish(update);
}
#endregion Actions
@@ -312,14 +312,14 @@ public class MainWindowViewModel : MyReactiveObject
private async Task RefreshServers()
{
- AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default);
+ AppEvents.ProfilesRefreshRequested.Publish();
await Task.Delay(200);
}
private void RefreshSubscriptions()
{
- AppEvents.SubscriptionsRefreshRequested.OnNext(Unit.Default);
+ AppEvents.SubscriptionsRefreshRequested.Publish();
}
#endregion Servers && Groups
@@ -451,7 +451,7 @@ public class MainWindowViewModel : MyReactiveObject
var ret = await _updateView?.Invoke(EViewAction.OptionSettingWindow, null);
if (ret == true)
{
- AppEvents.InboundDisplayRequested.OnNext(Unit.Default);
+ AppEvents.InboundDisplayRequested.Publish();
await Reload();
}
}
@@ -462,7 +462,7 @@ public class MainWindowViewModel : MyReactiveObject
if (ret == true)
{
await ConfigHandler.InitBuiltinRouting(_config);
- AppEvents.RoutingsMenuRefreshRequested.OnNext(Unit.Default);
+ AppEvents.RoutingsMenuRefreshRequested.Publish();
await Reload();
}
}
@@ -530,12 +530,12 @@ public class MainWindowViewModel : MyReactiveObject
await SysProxyHandler.UpdateSysProxy(_config, false);
await Task.Delay(1000);
});
- AppEvents.TestServerRequested.OnNext(Unit.Default);
+ AppEvents.TestServerRequested.Publish();
var showClashUI = _config.IsRunningCore(ECoreType.sing_box);
if (showClashUI)
{
- AppEvents.ProxiesReloadRequested.OnNext(Unit.Default);
+ AppEvents.ProxiesReloadRequested.Publish();
}
RxApp.MainThreadScheduler.Schedule(() => ReloadResult(showClashUI));
@@ -565,7 +565,7 @@ public class MainWindowViewModel : MyReactiveObject
{
if (_config.UiItem.AutoHideStartup)
{
- AppEvents.ShowHideWindowRequested.OnNext(false);
+ AppEvents.ShowHideWindowRequested.Publish(false);
}
await Task.CompletedTask;
}
@@ -578,7 +578,7 @@ public class MainWindowViewModel : MyReactiveObject
{
await ConfigHandler.ApplyRegionalPreset(_config, type);
await ConfigHandler.InitRouting(_config);
- AppEvents.RoutingsMenuRefreshRequested.OnNext(Unit.Default);
+ AppEvents.RoutingsMenuRefreshRequested.Publish();
await ConfigHandler.SaveConfig(_config);
await new UpdateService().UpdateGeoFileAll(_config, UpdateTaskHandler);
diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
index fcf3450d..b36c933c 100644
--- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
@@ -275,7 +275,7 @@ public class ProfilesViewModel : MyReactiveObject
private void Reload()
{
- AppEvents.ReloadRequested.OnNext(Unit.Default);
+ AppEvents.ReloadRequested.Publish();
}
public async Task SetSpeedTestResult(SpeedTestResult result)
@@ -361,7 +361,7 @@ public class ProfilesViewModel : MyReactiveObject
public async Task RefreshServers()
{
- AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default);
+ AppEvents.ProfilesRefreshRequested.Publish();
await Task.Delay(200);
}
diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
index 6dc795ef..26386e0f 100644
--- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
@@ -148,17 +148,17 @@ public class StatusBarViewModel : MyReactiveObject
NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () =>
{
- AppEvents.ShowHideWindowRequested.OnNext(null);
+ AppEvents.ShowHideWindowRequested.Publish(null);
await Task.CompletedTask;
});
ShowWindowCmd = ReactiveCommand.CreateFromTask(async () =>
{
- AppEvents.ShowHideWindowRequested.OnNext(true);
+ AppEvents.ShowHideWindowRequested.Publish(true);
await Task.CompletedTask;
});
HideWindowCmd = ReactiveCommand.CreateFromTask(async () =>
{
- AppEvents.ShowHideWindowRequested.OnNext(false);
+ AppEvents.ShowHideWindowRequested.Publish(false);
await Task.CompletedTask;
});
@@ -274,19 +274,19 @@ public class StatusBarViewModel : MyReactiveObject
private async Task AddServerViaClipboard()
{
- AppEvents.AddServerViaClipboardRequested.OnNext(Unit.Default);
+ AppEvents.AddServerViaClipboardRequested.Publish();
await Task.Delay(1000);
}
private async Task AddServerViaScan()
{
- AppEvents.AddServerViaScanRequested.OnNext(Unit.Default);
+ AppEvents.AddServerViaScanRequested.Publish();
await Task.Delay(1000);
}
private async Task UpdateSubscriptionProcess(bool blProxy)
{
- AppEvents.SubscriptionsUpdateRequested.OnNext(blProxy);
+ AppEvents.SubscriptionsUpdateRequested.Publish(blProxy);
await Task.Delay(1000);
}
@@ -348,7 +348,7 @@ public class StatusBarViewModel : MyReactiveObject
{
return;
}
- AppEvents.SetDefaultServerRequested.OnNext(SelectedServer.ID);
+ AppEvents.SetDefaultServerRequested.Publish(SelectedServer.ID);
}
public async Task TestServerAvailability()
@@ -449,7 +449,7 @@ public class StatusBarViewModel : MyReactiveObject
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
{
NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting);
- AppEvents.ReloadRequested.OnNext(Unit.Default);
+ AppEvents.ReloadRequested.Publish();
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
}
}
@@ -496,7 +496,7 @@ public class StatusBarViewModel : MyReactiveObject
}
}
await ConfigHandler.SaveConfig(_config);
- AppEvents.ReloadRequested.OnNext(Unit.Default);
+ AppEvents.ReloadRequested.Publish();
}
private bool AllowEnableTun()
diff --git a/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayN/v2rayN.Desktop/App.axaml.cs
index fa4779cf..a3468a01 100644
--- a/v2rayN/v2rayN.Desktop/App.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/App.axaml.cs
@@ -54,7 +54,7 @@ public partial class App : Application
{
if (desktop.MainWindow != null)
{
- AppEvents.AddServerViaClipboardRequested.OnNext(Unit.Default);
+ AppEvents.AddServerViaClipboardRequested.Publish();
await Task.Delay(1000);
}
}
diff --git a/v2rayN/v2rayN.Desktop/GlobalUsings.cs b/v2rayN/v2rayN.Desktop/GlobalUsings.cs
index 4f2b931d..aede90cc 100644
--- a/v2rayN/v2rayN.Desktop/GlobalUsings.cs
+++ b/v2rayN/v2rayN.Desktop/GlobalUsings.cs
@@ -2,6 +2,7 @@ global using ServiceLib;
global using ServiceLib.Base;
global using ServiceLib.Common;
global using ServiceLib.Enums;
+global using ServiceLib.Events;
global using ServiceLib.Handler;
global using ServiceLib.Manager;
global using ServiceLib.Models;
diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
index 86bd5d7b..6c56cefc 100644
--- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
@@ -253,7 +253,7 @@ public partial class MainWindow : WindowBase
case EGlobalHotkey.SystemProxySet:
case EGlobalHotkey.SystemProxyUnchanged:
case EGlobalHotkey.SystemProxyPac:
- AppEvents.SysProxyChangeRequested.OnNext((ESysProxyType)((int)e - 1));
+ AppEvents.SysProxyChangeRequested.Publish((ESysProxyType)((int)e - 1));
break;
}
}
diff --git a/v2rayN/v2rayN/GlobalUsings.cs b/v2rayN/v2rayN/GlobalUsings.cs
index 4f2b931d..aede90cc 100644
--- a/v2rayN/v2rayN/GlobalUsings.cs
+++ b/v2rayN/v2rayN/GlobalUsings.cs
@@ -2,6 +2,7 @@ global using ServiceLib;
global using ServiceLib.Base;
global using ServiceLib.Common;
global using ServiceLib.Enums;
+global using ServiceLib.Events;
global using ServiceLib.Handler;
global using ServiceLib.Manager;
global using ServiceLib.Models;
diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs
index 1d0d530c..39bc3985 100644
--- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs
@@ -241,7 +241,7 @@ public partial class MainWindow
case EGlobalHotkey.SystemProxySet:
case EGlobalHotkey.SystemProxyUnchanged:
case EGlobalHotkey.SystemProxyPac:
- AppEvents.SysProxyChangeRequested.OnNext((ESysProxyType)((int)e - 1));
+ AppEvents.SysProxyChangeRequested.Publish((ESysProxyType)((int)e - 1));
break;
}
}
From 21a773f40076cb4df47c6d7068560f10c85d1921 Mon Sep 17 00:00:00 2001
From: JieXu
Date: Fri, 26 Sep 2025 13:55:35 +0800
Subject: [PATCH 017/132] Update MsgView.axaml.cs Plan C (#8035)
* Add avaloniaEdit for test
* Adjust avaloniaEdit
* Optimize and improve message function
* Update build-linux.yml
* Update MsgView.axaml
* Update MsgView.axaml.cs
---------
Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
---
.github/workflows/build-linux.yml | 2 +-
v2rayN/Directory.Packages.props | 4 +-
v2rayN/ServiceLib/ViewModels/MsgViewModel.cs | 66 +++++++++----------
v2rayN/v2rayN.Desktop/App.axaml | 1 +
.../ViewModels/ThemeSettingViewModel.cs | 7 +-
v2rayN/v2rayN.Desktop/Views/MsgView.axaml | 62 ++++++++---------
v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 35 ++++++----
v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj | 2 +
v2rayN/v2rayN/Views/MsgView.xaml.cs | 11 +++-
9 files changed, 107 insertions(+), 83 deletions(-)
diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml
index 40e9c953..d5f9b1c5 100644
--- a/.github/workflows/build-linux.yml
+++ b/.github/workflows/build-linux.yml
@@ -22,7 +22,7 @@ jobs:
matrix:
configuration: [Release]
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- name: Checkout
diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props
index 24484112..9dcfbe61 100644
--- a/v2rayN/Directory.Packages.props
+++ b/v2rayN/Directory.Packages.props
@@ -5,6 +5,7 @@
false
+
@@ -19,6 +20,7 @@
+
@@ -27,4 +29,4 @@
-
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs
index 8fc62dfe..8b3467bd 100644
--- a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs
@@ -1,5 +1,6 @@
using System.Collections.Concurrent;
using System.Reactive.Linq;
+using System.Text;
using System.Text.RegularExpressions;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
@@ -9,9 +10,9 @@ namespace ServiceLib.ViewModels;
public class MsgViewModel : MyReactiveObject
{
private readonly ConcurrentQueue _queueMsg = new();
- private readonly int _numMaxMsg = 500;
- private bool _lastMsgFilterNotAvailable;
- private bool _blLockShow = false;
+ private volatile bool _lastMsgFilterNotAvailable;
+ private int _showLock = 0; // 0 = unlocked, 1 = locked
+ public int NumMaxMsg { get; } = 50;
[Reactive]
public string MsgFilter { get; set; }
@@ -33,46 +34,52 @@ public class MsgViewModel : MyReactiveObject
this.WhenAnyValue(
x => x.AutoRefresh,
y => y == true)
- .Subscribe(c => { _config.MsgUIItem.AutoRefresh = AutoRefresh; });
+ .Subscribe(c => _config.MsgUIItem.AutoRefresh = AutoRefresh);
AppEvents.SendMsgViewRequested
.AsObservable()
//.ObserveOn(RxApp.MainThreadScheduler)
- .Subscribe(async content => await AppendQueueMsg(content));
+ .Subscribe(content => _ = AppendQueueMsg(content));
}
private async Task AppendQueueMsg(string msg)
{
- //if (msg == Global.CommandClearMsg)
- //{
- // ClearMsg();
- // return;
- //}
if (AutoRefresh == false)
{
return;
}
- _ = EnqueueQueueMsg(msg);
- if (_blLockShow)
- {
- return;
- }
+ EnqueueQueueMsg(msg);
+
if (!_config.UiItem.ShowInTaskbar)
{
return;
}
- _blLockShow = true;
+ if (Interlocked.CompareExchange(ref _showLock, 1, 0) != 0)
+ {
+ return;
+ }
- await Task.Delay(500);
- var txt = string.Join("", _queueMsg.ToArray());
- await _updateView?.Invoke(EViewAction.DispatcherShowMsg, txt);
+ try
+ {
+ await Task.Delay(500).ConfigureAwait(false);
- _blLockShow = false;
+ var sb = new StringBuilder();
+ while (_queueMsg.TryDequeue(out var line))
+ {
+ sb.Append(line);
+ }
+
+ await _updateView?.Invoke(EViewAction.DispatcherShowMsg, sb.ToString());
+ }
+ finally
+ {
+ Interlocked.Exchange(ref _showLock, 0);
+ }
}
- private async Task EnqueueQueueMsg(string msg)
+ private void EnqueueQueueMsg(string msg)
{
//filter msg
if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable)
@@ -91,26 +98,17 @@ public class MsgViewModel : MyReactiveObject
}
}
- //Enqueue
- if (_queueMsg.Count > _numMaxMsg)
- {
- for (int k = 0; k < _queueMsg.Count - _numMaxMsg; k++)
- {
- _queueMsg.TryDequeue(out _);
- }
- }
_queueMsg.Enqueue(msg);
if (!msg.EndsWith(Environment.NewLine))
{
_queueMsg.Enqueue(Environment.NewLine);
}
- await Task.CompletedTask;
}
- public void ClearMsg()
- {
- _queueMsg.Clear();
- }
+ //public void ClearMsg()
+ //{
+ // _queueMsg.Clear();
+ //}
private void DoMsgFilter()
{
diff --git a/v2rayN/v2rayN.Desktop/App.axaml b/v2rayN/v2rayN.Desktop/App.axaml
index 8b61125f..222ab488 100644
--- a/v2rayN/v2rayN.Desktop/App.axaml
+++ b/v2rayN/v2rayN.Desktop/App.axaml
@@ -11,6 +11,7 @@
RequestedThemeVariant="Default">
+
diff --git a/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs b/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs
index 3473cf23..ae12fa07 100644
--- a/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs
+++ b/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs
@@ -5,6 +5,7 @@ using Avalonia.Controls.Notifications;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using Avalonia.Styling;
+using AvaloniaEdit;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Semi.Avalonia;
@@ -112,7 +113,8 @@ public class ThemeSettingViewModel : MyReactiveObject
x.OfType(),
x.OfType(),
x.OfType(),
- x.OfType()
+ x.OfType(),
+ x.OfType()
));
style.Add(new Setter()
{
@@ -153,7 +155,8 @@ public class ThemeSettingViewModel : MyReactiveObject
x.OfType(),
x.OfType(),
x.OfType(),
- x.OfType()
+ x.OfType(),
+ x.OfType()
));
style.Add(new Setter()
{
diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml
index b6ca81a1..2f7ad807 100644
--- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml
@@ -2,6 +2,7 @@
x:Class="v2rayN.Desktop.Views.MsgView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:avaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
@@ -70,35 +71,36 @@
Theme="{DynamicResource SimpleToggleSwitch}" />
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
index e2d21101..f7b2f9b9 100644
--- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
@@ -1,5 +1,4 @@
using System.Reactive.Disposables;
-using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
using Avalonia.Threading;
@@ -10,13 +9,12 @@ namespace v2rayN.Desktop.Views;
public partial class MsgView : ReactiveUserControl
{
- private readonly ScrollViewer _scrollViewer;
+ private const int MaxLines = 350;
+ private const int KeepLines = 320;
public MsgView()
{
InitializeComponent();
- _scrollViewer = this.FindControl("msgScrollViewer");
-
ViewModel = new MsgViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
@@ -34,9 +32,8 @@ public partial class MsgView : ReactiveUserControl
if (obj is null)
return false;
- Dispatcher.UIThread.Post(() =>
- ShowMsg(obj),
- DispatcherPriority.ApplicationIdle);
+ Dispatcher.UIThread.Post(() => ShowMsg(obj),
+ DispatcherPriority.ApplicationIdle);
break;
}
return await Task.FromResult(true);
@@ -44,23 +41,35 @@ public partial class MsgView : ReactiveUserControl
private void ShowMsg(object msg)
{
- txtMsg.Text = msg.ToString();
+ txtMsg.AppendText(msg.ToString());
+
+ if (txtMsg.Document.LineCount > MaxLines)
+ {
+ var lc = txtMsg.Document.LineCount;
+ var cutLineNumber = lc - KeepLines;
+ var cutLine = txtMsg.Document.GetLineByNumber(cutLineNumber);
+ txtMsg.Document.Remove(0, cutLine.Offset);
+ }
+
if (togScrollToEnd.IsChecked ?? true)
{
- _scrollViewer?.ScrollToEnd();
+ txtMsg.ScrollToEnd();
}
}
public void ClearMsg()
{
- ViewModel?.ClearMsg();
- txtMsg.Text = "";
+ txtMsg.Text = string.Empty;
+ txtMsg.AppendText("----- Message cleared -----\n");
}
private void menuMsgViewSelectAll_Click(object? sender, RoutedEventArgs e)
{
- txtMsg.Focus();
- txtMsg.SelectAll();
+ Dispatcher.UIThread.Post(() =>
+ {
+ txtMsg.TextArea.Focus();
+ txtMsg.SelectAll();
+ }, DispatcherPriority.Render);
}
private async void menuMsgViewCopy_Click(object? sender, RoutedEventArgs e)
diff --git a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj
index 8c274a69..f6ff5735 100644
--- a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj
+++ b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj
@@ -9,6 +9,7 @@
+
true
@@ -17,6 +18,7 @@
+
true
diff --git a/v2rayN/v2rayN/Views/MsgView.xaml.cs b/v2rayN/v2rayN/Views/MsgView.xaml.cs
index 3cc1fdfa..af8eb5f4 100644
--- a/v2rayN/v2rayN/Views/MsgView.xaml.cs
+++ b/v2rayN/v2rayN/Views/MsgView.xaml.cs
@@ -48,18 +48,25 @@ public partial class MsgView
private void ShowMsg(object msg)
{
txtMsg.BeginChange();
- txtMsg.Text = msg.ToString();
+
+ if (txtMsg.LineCount > ViewModel?.NumMaxMsg)
+ {
+ ClearMsg();
+ }
+
+ txtMsg.AppendText(msg.ToString());
if (togScrollToEnd.IsChecked ?? true)
{
txtMsg.ScrollToEnd();
}
+
txtMsg.EndChange();
}
public void ClearMsg()
{
- ViewModel?.ClearMsg();
txtMsg.Clear();
+ txtMsg.AppendText("----- Message cleared -----\n");
}
private void menuMsgViewSelectAll_Click(object sender, System.Windows.RoutedEventArgs e)
From 326bf334e761d90a8b871f7acbfd231787d75095 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 26 Sep 2025 15:07:33 +0800
Subject: [PATCH 018/132] Optimize and improve MsgView
---
v2rayN/ServiceLib/ViewModels/MsgViewModel.cs | 2 +-
v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 21 ++++++++++++--------
2 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs
index 8b3467bd..e9d89b94 100644
--- a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs
@@ -12,7 +12,7 @@ public class MsgViewModel : MyReactiveObject
private readonly ConcurrentQueue _queueMsg = new();
private volatile bool _lastMsgFilterNotAvailable;
private int _showLock = 0; // 0 = unlocked, 1 = locked
- public int NumMaxMsg { get; } = 50;
+ public int NumMaxMsg { get; } = 500;
[Reactive]
public string MsgFilter { get; set; }
diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
index f7b2f9b9..637579ee 100644
--- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
@@ -9,8 +9,7 @@ namespace v2rayN.Desktop.Views;
public partial class MsgView : ReactiveUserControl
{
- private const int MaxLines = 350;
- private const int KeepLines = 320;
+ //private const int KeepLines = 30;
public MsgView()
{
@@ -41,20 +40,26 @@ public partial class MsgView : ReactiveUserControl
private void ShowMsg(object msg)
{
- txtMsg.AppendText(msg.ToString());
+ txtMsg.BeginChange();
- if (txtMsg.Document.LineCount > MaxLines)
+ //var lineCount = txtMsg.LineCount;
+ //if (lineCount > ViewModel?.NumMaxMsg)
+ //{
+ // var cutLine = txtMsg.Document.GetLineByNumber(lineCount - KeepLines);
+ // txtMsg.Document.Remove(0, cutLine.Offset);
+ //}
+ if (txtMsg.LineCount > ViewModel?.NumMaxMsg)
{
- var lc = txtMsg.Document.LineCount;
- var cutLineNumber = lc - KeepLines;
- var cutLine = txtMsg.Document.GetLineByNumber(cutLineNumber);
- txtMsg.Document.Remove(0, cutLine.Offset);
+ ClearMsg();
}
+ txtMsg.AppendText(msg.ToString());
if (togScrollToEnd.IsChecked ?? true)
{
txtMsg.ScrollToEnd();
}
+
+ txtMsg.EndChange();
}
public void ClearMsg()
From a652fd879b6fcef1842c6c07848772fb76cb6e86 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 26 Sep 2025 15:29:46 +0800
Subject: [PATCH 019/132] Added simple highlight function to the message view
---
v2rayN/ServiceLib/Global.cs | 8 ++
.../Common/TextEditorKeywordHighlighter.cs | 129 ++++++++++++++++++
v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 6 +
3 files changed, 143 insertions(+)
create mode 100644 v2rayN/v2rayN.Desktop/Common/TextEditorKeywordHighlighter.cs
diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs
index b45a6eb3..6c4a4362 100644
--- a/v2rayN/ServiceLib/Global.cs
+++ b/v2rayN/ServiceLib/Global.cs
@@ -449,6 +449,14 @@ public class Global
"none"
];
+ public static readonly Dictionary LogLevelColors = new()
+ {
+ { "debug", "#6C757D" },
+ { "info", "#2ECC71" },
+ { "warning", "#FFA500" },
+ { "error", "#E74C3C" },
+ };
+
public static readonly List InboundTags =
[
"socks",
diff --git a/v2rayN/v2rayN.Desktop/Common/TextEditorKeywordHighlighter.cs b/v2rayN/v2rayN.Desktop/Common/TextEditorKeywordHighlighter.cs
new file mode 100644
index 00000000..af1de3d0
--- /dev/null
+++ b/v2rayN/v2rayN.Desktop/Common/TextEditorKeywordHighlighter.cs
@@ -0,0 +1,129 @@
+using Avalonia.Media;
+using AvaloniaEdit;
+using AvaloniaEdit.Document;
+using AvaloniaEdit.Rendering;
+
+namespace v2rayN.Desktop.Common;
+
+public class KeywordColorizer : DocumentColorizingTransformer
+{
+ private readonly string[] _keywords;
+ private readonly Dictionary _brushMap;
+
+ public KeywordColorizer(IDictionary keywordBrushMap)
+ {
+ if (keywordBrushMap == null || keywordBrushMap.Count == 0)
+ {
+ throw new ArgumentException("keywordBrushMap must not be null or empty", nameof(keywordBrushMap));
+ }
+
+ _brushMap = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ foreach (var kvp in keywordBrushMap)
+ {
+ if (string.IsNullOrEmpty(kvp.Key) || kvp.Value == null)
+ {
+ continue;
+ }
+
+ if (!_brushMap.ContainsKey(kvp.Key))
+ {
+ _brushMap[kvp.Key] = kvp.Value;
+ }
+ }
+
+ if (_brushMap.Count == 0)
+ {
+ throw new ArgumentException("keywordBrushMap must contain at least one non-empty key with a non-null brush", nameof(keywordBrushMap));
+ }
+
+ _keywords = _brushMap.Keys.ToArray();
+ }
+
+ protected override void ColorizeLine(DocumentLine line)
+ {
+ var text = CurrentContext.Document.GetText(line);
+ if (string.IsNullOrEmpty(text))
+ {
+ return;
+ }
+
+ foreach (var kw in _keywords)
+ {
+ if (string.IsNullOrEmpty(kw))
+ {
+ continue;
+ }
+
+ var searchStart = 0;
+ while (true)
+ {
+ var idx = text.IndexOf(kw, searchStart, StringComparison.OrdinalIgnoreCase);
+ if (idx < 0)
+ {
+ break;
+ }
+
+ var kwEndIndex = idx + kw.Length;
+ if (IsWordCharBefore(text, idx) || IsWordCharAfter(text, kwEndIndex))
+ {
+ searchStart = idx + Math.Max(1, kw.Length);
+ continue;
+ }
+
+ var start = line.Offset + idx;
+ var end = start + kw.Length;
+
+ if (_brushMap.TryGetValue(kw, out var brush) && brush != null)
+ {
+ ChangeLinePart(start, end, element => element.TextRunProperties.SetForegroundBrush(brush));
+ }
+
+ searchStart = idx + Math.Max(1, kw.Length);
+ }
+ }
+ }
+
+ private static bool IsWordCharBefore(string text, int idx)
+ {
+ if (idx <= 0)
+ {
+ return false;
+ }
+
+ var c = text[idx - 1];
+ return char.IsLetterOrDigit(c) || c == '_';
+ }
+
+ private static bool IsWordCharAfter(string text, int idx)
+ {
+ if (idx >= text.Length)
+ {
+ return false;
+ }
+
+ var c = text[idx];
+ return char.IsLetterOrDigit(c) || c == '_';
+ }
+}
+
+public static class TextEditorKeywordHighlighter
+{
+ public static void Attach(TextEditor editor, IDictionary keywordBrushMap)
+ {
+ ArgumentNullException.ThrowIfNull(editor);
+
+ if (keywordBrushMap == null || keywordBrushMap.Count == 0)
+ {
+ return;
+ }
+
+ if (editor.TextArea?.TextView?.LineTransformers?.OfType().Any() == true)
+ {
+ return;
+ }
+
+ var colorizer = new KeywordColorizer(keywordBrushMap);
+ editor.TextArea.TextView.LineTransformers.Add(colorizer);
+ editor.TextArea.TextView.InvalidateVisual();
+ }
+}
diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
index 637579ee..6a279284 100644
--- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
@@ -1,5 +1,6 @@
using System.Reactive.Disposables;
using Avalonia.Interactivity;
+using Avalonia.Media;
using Avalonia.ReactiveUI;
using Avalonia.Threading;
using ReactiveUI;
@@ -21,6 +22,11 @@ public partial class MsgView : ReactiveUserControl
this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
});
+
+ TextEditorKeywordHighlighter.Attach(txtMsg, Global.LogLevelColors.ToDictionary(
+ kv => kv.Key,
+ kv => (IBrush)new SolidColorBrush(Color.Parse(kv.Value))
+ ));
}
private async Task UpdateViewHandler(EViewAction action, object? obj)
From 03d5b7a05b36ef002ed2439e703539e290444cdd Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 26 Sep 2025 17:11:48 +0800
Subject: [PATCH 020/132] Bug fix
---
v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 6 +-----
v2rayN/v2rayN/Views/MsgView.xaml.cs | 4 ----
2 files changed, 1 insertion(+), 9 deletions(-)
diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
index 6a279284..a49253c3 100644
--- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
@@ -46,8 +46,6 @@ public partial class MsgView : ReactiveUserControl
private void ShowMsg(object msg)
{
- txtMsg.BeginChange();
-
//var lineCount = txtMsg.LineCount;
//if (lineCount > ViewModel?.NumMaxMsg)
//{
@@ -64,13 +62,11 @@ public partial class MsgView : ReactiveUserControl
{
txtMsg.ScrollToEnd();
}
-
- txtMsg.EndChange();
}
public void ClearMsg()
{
- txtMsg.Text = string.Empty;
+ txtMsg.Clear();
txtMsg.AppendText("----- Message cleared -----\n");
}
diff --git a/v2rayN/v2rayN/Views/MsgView.xaml.cs b/v2rayN/v2rayN/Views/MsgView.xaml.cs
index af8eb5f4..449597cc 100644
--- a/v2rayN/v2rayN/Views/MsgView.xaml.cs
+++ b/v2rayN/v2rayN/Views/MsgView.xaml.cs
@@ -47,8 +47,6 @@ public partial class MsgView
private void ShowMsg(object msg)
{
- txtMsg.BeginChange();
-
if (txtMsg.LineCount > ViewModel?.NumMaxMsg)
{
ClearMsg();
@@ -59,8 +57,6 @@ public partial class MsgView
{
txtMsg.ScrollToEnd();
}
-
- txtMsg.EndChange();
}
public void ClearMsg()
From dc4611a2589a2a9f79fbdee1ba3761d316b2eead Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 26 Sep 2025 20:36:27 +0800
Subject: [PATCH 021/132] Adjust qrcode width
---
v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml | 2 +-
v2rayN/v2rayN/Views/QrcodeView.xaml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml b/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml
index b67a7a1f..350cb06a 100644
--- a/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml
@@ -10,7 +10,7 @@
mc:Ignorable="d">
- 500
+ 400
diff --git a/v2rayN/v2rayN/Views/QrcodeView.xaml b/v2rayN/v2rayN/Views/QrcodeView.xaml
index b2dcdc3d..80aaae7d 100644
--- a/v2rayN/v2rayN/Views/QrcodeView.xaml
+++ b/v2rayN/v2rayN/Views/QrcodeView.xaml
@@ -12,7 +12,7 @@
Style="{StaticResource ViewGlobal}"
mc:Ignorable="d">
- 500
+ 400
From ebb95b5ee8374d05058a119a9454d6aaf747a426 Mon Sep 17 00:00:00 2001
From: JieXu
Date: Sat, 27 Sep 2025 17:02:49 +0800
Subject: [PATCH 022/132] Update MsgView.axaml.cs (#8042)
---
v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
index a49253c3..1f7665ca 100644
--- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
@@ -15,6 +15,7 @@ public partial class MsgView : ReactiveUserControl
public MsgView()
{
InitializeComponent();
+ txtMsg.TextArea.TextView.Options.EnableHyperlinks = false;
ViewModel = new MsgViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
From 46edd8f9a4ac65097692f869b8c5231474689e76 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 27 Sep 2025 18:07:20 +0800
Subject: [PATCH 023/132] Bug fix
---
v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs | 2 +-
v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
index be8898ae..a6a6bd31 100644
--- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
@@ -248,7 +248,7 @@ public class MainWindowViewModel : MyReactiveObject
{
_config.UiItem.ShowInTaskbar = true;
- await ConfigHandler.InitBuiltinRouting(_config);
+ //await ConfigHandler.InitBuiltinRouting(_config);
await ConfigHandler.InitBuiltinDNS(_config);
await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
await ProfileExManager.Instance.Init();
diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
index 26386e0f..9742cfe6 100644
--- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
@@ -238,6 +238,7 @@ public class StatusBarViewModel : MyReactiveObject
private async Task Init()
{
+ await ConfigHandler.InitBuiltinRouting(_config);
await RefreshRoutingsMenu();
await InboundDisplayStatus();
await ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, true);
From b25d4d57bd10ac3448f095b6a40bd3d1feaf1742 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 27 Sep 2025 19:46:31 +0800
Subject: [PATCH 024/132] Fix ProfilesSelectWindow
---
v2rayN/v2rayN.Desktop/Views/ProfilesSelectWindow.axaml.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/v2rayN/v2rayN.Desktop/Views/ProfilesSelectWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/ProfilesSelectWindow.axaml.cs
index d597e567..ac562758 100644
--- a/v2rayN/v2rayN.Desktop/Views/ProfilesSelectWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/ProfilesSelectWindow.axaml.cs
@@ -3,13 +3,13 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
-using Avalonia.ReactiveUI;
using Avalonia.VisualTree;
using ReactiveUI;
+using v2rayN.Desktop.Base;
namespace v2rayN.Desktop.Views;
-public partial class ProfilesSelectWindow : ReactiveWindow
+public partial class ProfilesSelectWindow : WindowBase
{
private static Config _config;
From 7f07279a4c20b1f15a765fe66f8496d96b7ff94b Mon Sep 17 00:00:00 2001
From: Wydy <1937986+wydy@users.noreply.github.com>
Date: Sun, 28 Sep 2025 19:08:29 +0800
Subject: [PATCH 025/132] Update pac (#7991)
---
v2rayN/ServiceLib/Sample/pac | 336 +++++++++++++++--------------------
1 file changed, 145 insertions(+), 191 deletions(-)
diff --git a/v2rayN/ServiceLib/Sample/pac b/v2rayN/ServiceLib/Sample/pac
index 1378c49f..f4f5939b 100644
--- a/v2rayN/ServiceLib/Sample/pac
+++ b/v2rayN/ServiceLib/Sample/pac
@@ -7,12 +7,12 @@ var rules = [
"030buy.com",
"0rz.tw",
"1-apple.com.tw",
- "10.tt",
"1000giri.net",
"100ke.org",
"10beasts.net",
"10conditionsoflove.com",
"10musume.com",
+ "111666.best",
"123rf.com",
"12bet.com",
"12vpn.com",
@@ -29,7 +29,6 @@ var rules = [
"177pic.info",
"17t17p.com",
"18board.com",
- "18board.info",
"18comic.org",
"18comic.vip",
"18hmanga.click",
@@ -42,20 +41,18 @@ var rules = [
"1984bbs.com",
"1984bbs.org",
"1991way.com",
- "1998cdp.org",
"1bao.org",
"1dumb.com",
"1e100.net",
"1eew.com",
"1lib.domains",
+ "1lib.sk",
"1mobile.com",
- "1mobile.tw",
"1point3acres.com",
"1pondo.tv",
"2-hand.info",
"2000fun.com",
"2008xianzhang.info",
- "2017.hk",
"2021hkcharter.com",
"2047.name",
"2047.one",
@@ -68,7 +65,6 @@ var rules = [
"233abc.com",
"233v2.com",
"24hrs.ca",
- "24smile.org",
"25u.com",
"2du5.com",
"2lipstube.com",
@@ -87,11 +83,11 @@ var rules = [
"3ren.ca",
"3tui.net",
"404museum.com",
- "43110.cf",
"466453.com",
"4bluestones.biz",
"4chan.com",
"4dq.com",
+ "4everland.io",
"4everproxy.com",
"4gtv.tv",
"4irc.com",
@@ -109,7 +105,7 @@ var rules = [
"5278.cc",
"5299.tv",
"55comic.com",
- "5aimiku.com",
+ "5657.com.tw",
"5i01.com",
"5isotoi5.org",
"5maodang.com",
@@ -124,6 +120,7 @@ var rules = [
"666kb.com",
"666pool.cn",
"69shu.com",
+ "69shuba.com",
"69shuba.cx",
"6do.news",
"6do.world",
@@ -146,8 +143,7 @@ var rules = [
"8news.com.tw",
"8world.com",
"8z1.net",
- "9001700.com",
- "908taiwan.org",
+ "91dasai.com",
"91jinman.com",
"91porn.com",
"91porny.com",
@@ -169,16 +165,13 @@ var rules = [
"abc.net.au",
"abc.xyz",
"abchinese.com",
- "abclite.net",
"abebooks.co.uk",
"abebooks.com",
"ablwang.com",
"aboluowang.com",
- "about.google",
"about.me",
- "aboutgfw.com",
+ "abplive.com",
"abs.edu",
- "ac.jp",
"acast.com",
"accim.org",
"accountkit.com",
@@ -226,7 +219,6 @@ var rules = [
"agro.hk",
"ai-kan.net",
"ai-wen.net",
- "ai.google",
"aiosearch.com",
"aiph.net",
"airasia.com",
@@ -308,9 +300,9 @@ var rules = [
"amnesty.tw",
"amnestyusa.org",
"amnyemachen.org",
- "amoiist.com",
"ampproject.org",
"amtb-taipei.org",
+ "amuletmc.com",
"anchor.fm",
"anchorfree.com",
"ancsconf.org",
@@ -363,8 +355,8 @@ var rules = [
"aol.co.uk",
"aol.com",
"aolnews.com",
+ "aomedia.org",
"aomiwang.com",
- "ap.org",
"apartmentratings.com",
"apartments.com",
"apat1989.org",
@@ -384,6 +376,7 @@ var rules = [
"aplusvpn.com",
"appadvice.com",
"appbrain.com",
+ "appdefensealliance.dev",
"appdownloader.net",
"appledaily.com",
"appledaily.com.hk",
@@ -412,12 +405,14 @@ var rules = [
"arethusa.su",
"arlingtoncemetery.mil",
"army.mil",
+ "arstechnica.net",
"art4tibet1998.org",
"arte.tv",
"artofpeacefoundation.org",
"artstation.com",
"artsy.net",
"arunachalforests.gov.in",
+ "arvanstorage.ir",
"asacp.org",
"asdfg.jp",
"asg.to",
@@ -425,10 +420,7 @@ var rules = [
"asiaharvest.org",
"asianage.com",
"asianews.it",
- "asianfreeforum.com",
"asiansexdiary.com",
- "asianspiss.com",
- "asianwomensfilm.de",
"asiaone.com",
"asiatgp.com",
"asiatimes.com",
@@ -447,13 +439,11 @@ var rules = [
"atgfw.org",
"athenaeizou.com",
"atlanta168.com",
- "atlaspost.com",
"atnext.com",
"audacy.com",
"audionow.com",
"authorizeddns.net",
"authorizeddns.org",
- "authorizeddns.us",
"autodraw.com",
"av-e-body.com",
"av.com",
@@ -480,7 +470,6 @@ var rules = [
"azerbaycan.tv",
"azerimix.com",
"azirevpn.com",
- "azubu.tv",
"azurewebsites.net",
"b-cdn.net",
"b-ok.cc",
@@ -525,7 +514,6 @@ var rules = [
"bartvpn.com",
"bastillepost.com",
"bayvoice.net",
- "baywords.com",
"bb-chat.tv",
"bbc.co.uk",
"bbc.com",
@@ -583,6 +571,7 @@ var rules = [
"bet365.com",
"betaclouds.net",
"betfair.com",
+ "betterhash.net",
"betternet.co",
"bettervpn.com",
"bettween.com",
@@ -616,6 +605,7 @@ var rules = [
"biliworld.com",
"billypan.com",
"binance.com",
+ "binance.org",
"binance.us",
"binancezh.cc",
"bing.com",
@@ -633,7 +623,6 @@ var rules = [
"bitfinex.com",
"bitget.com",
"bithumb.com",
- "bitinka.com.ar",
"bitmex.com",
"bitshare.com",
"bitsnoop.com",
@@ -648,6 +637,7 @@ var rules = [
"bl-doujinsouko.com",
"blacked.com",
"blacklogic.com",
+ "blackmagicdesign.com",
"blackvpn.com",
"blewpass.com",
"blingblingsquad.net",
@@ -660,7 +650,6 @@ var rules = [
"blockless.com",
"blocktempo.com",
"blog.de",
- "blog.google",
"blog.jp",
"blogblog.com",
"blogcatalog.com",
@@ -756,17 +745,18 @@ var rules = [
"bodog88.com",
"bolehvpn.net",
"bonbonme.com",
- "bonbonsex.com",
"bonfoundation.org",
"bongacams.com",
"boobstagram.com",
"book.com.tw",
"bookdepository.com",
"bookepub.com",
+ "booklive.jp",
+ "bookmeter.com",
"books.com.tw",
"booktopia.com.au",
"bookwalker.com.tw",
- "boomssr.com",
+ "bookwalker.jp",
"bootstrapcdn.com",
"borgenmagazine.com",
"bot.nu",
@@ -796,11 +786,9 @@ var rules = [
"breaking911.com",
"breakingtweets.com",
"breakwall.net",
- "briefdream.com",
"briian.com",
"brill.com",
"brizzly.com",
- "brkmd.com",
"broadbook.com",
"broadpressinc.com",
"brockbbs.com",
@@ -811,6 +799,8 @@ var rules = [
"bsky.network",
"bsky.social",
"bt2mag.com",
+ "bt4g.org",
+ "bt4gprx.com",
"bt95.com",
"btaia.com",
"btbit.net",
@@ -826,6 +816,7 @@ var rules = [
"btguard.com",
"btku.me",
"btku.org",
+ "btloader.com",
"btspread.com",
"btsynckeys.com",
"budaedu.org",
@@ -868,6 +859,7 @@ var rules = [
"c-est-simple.com",
"c-span.org",
"c-spanvideo.org",
+ "c.gle",
"c100tibet.org",
"c2cx.com",
"c3pool.com",
@@ -881,15 +873,12 @@ var rules = [
"cactusvpn.com",
"cafepress.com",
"cahr.org.tw",
- "caijinglengyan.com",
"calameo.com",
"calebelston.com",
"calendarz.com",
"calgarychinese.ca",
"calgarychinese.com",
"calgarychinese.net",
- "calibre-ebook.com",
- "caltech.edu",
"cam4.com",
"cam4.jp",
"cam4.sg",
@@ -1021,7 +1010,6 @@ var rules = [
"chicagoncmtv.com",
"china-mmm.jp.net",
"china-mmm.net",
- "china-mmm.sa.com",
"china-review.com.ua",
"china-week.com",
"china101.com",
@@ -1104,7 +1092,6 @@ var rules = [
"chrlawyers.hk",
"chrome.com",
"chromecast.com",
- "chromeenterprise.google",
"chromeexperiments.com",
"chromercise.com",
"chromestatus.com",
@@ -1118,7 +1105,9 @@ var rules = [
"chushigangdrug.ch",
"ci-en.jp",
"cia.gov",
+ "cici.com",
"ciciai.com",
+ "ciciaicdn.com",
"cienen.com",
"cineastentreff.de",
"cipfg.org",
@@ -1139,7 +1128,6 @@ var rules = [
"civilhrfront.org",
"civiliangunner.com",
"civilmedia.tw",
- "civisec.org",
"civitai.com",
"cixiaoya.club",
"cjb.net",
@@ -1162,8 +1150,8 @@ var rules = [
"clips4sale.com",
"cloakpoint.com",
"cloudcone.com",
+ "cloudflare-dns.com",
"cloudflare-ipfs.com",
- "cloudfront.net",
"cloudfunctions.net",
"cloudokyo.cloud",
"club1069.com",
@@ -1192,17 +1180,15 @@ var rules = [
"cnpolitics.org",
"cnproxy.com",
"cnyes.com",
- "co.tv",
"coat.co.jp",
"cobinhood.com",
"cochina.co",
"cochina.org",
"code1984.com",
- "codeplex.com",
"codeshare.io",
"codeskulptor.org",
"cofacts.tw",
- "coin2co.in",
+ "coffeemanga.to",
"coinbase.com",
"coinbene.com",
"coinegg.com",
@@ -1217,12 +1203,10 @@ var rules = [
"colacloud.net",
"collateralmurder.com",
"collateralmurder.org",
- "com.google",
"com.uk",
"comedycentral.com",
"comefromchina.com",
"comic-mega.me",
- "comico.tw",
"commandarms.com",
"comments.app",
"commentshk.com",
@@ -1251,10 +1235,13 @@ var rules = [
"cotweet.com",
"counter.social",
"coursehero.com",
+ "covenantswatch.org.tw",
"coze.com",
"cpj.org",
+ "cpu-monkey.com",
"cq99.us",
"crackle.com",
+ "crashlytics.com",
"crazypool.org",
"crazys.cc",
"crazyshit.com",
@@ -1264,7 +1251,6 @@ var rules = [
"creaders.net",
"creadersnet.com",
"creativelab5.com",
- "crisisresponse.google",
"cristyli.com",
"crocotube.com",
"crossfire.co.kr",
@@ -1287,6 +1273,7 @@ var rules = [
"ct.org.tw",
"ctao.org",
"ctfriend.net",
+ "ctinews.com",
"ctitv.com.tw",
"ctowc.org",
"cts.com.tw",
@@ -1312,7 +1299,6 @@ var rules = [
"cz.cc",
"d-fukyu.com",
"d.cash",
- "d0z.net",
"d100.net",
"d2bay.com",
"d2pass.com",
@@ -1407,10 +1393,11 @@ var rules = [
"delicious.com",
"democrats.org",
"demosisto.hk",
+ "deno.com",
+ "deno.dev",
"depositphotos.com",
"derekhsu.homeip.net",
"desc.se",
- "design.google",
"desipro.de",
"dessci.com",
"destroy-china.jp",
@@ -1435,7 +1422,6 @@ var rules = [
"digitalnomadsproject.org",
"diigo.com",
"dilber.se",
- "dingchin.com.tw",
"dipity.com",
"directcreative.com",
"discoins.com",
@@ -1474,7 +1460,6 @@ var rules = [
"dmm.com",
"dns-dns.com",
"dns-stuff.com",
- "dns.google",
"dns04.com",
"dns05.com",
"dns1.us",
@@ -1496,10 +1481,8 @@ var rules = [
"dok-forum.net",
"dolc.de",
"dolf.org.hk",
- "dollf.com",
"domain.club.tw",
"domain.glass",
- "domains.google",
"domaintoday.com.au",
"donga.com",
"dongtaiwang.com",
@@ -1508,6 +1491,7 @@ var rules = [
"donmai.us",
"dontfilter.us",
"dontmovetochina.com",
+ "doom9.org",
"doosho.com",
"doourbest.org",
"dorjeshugden.com",
@@ -1518,10 +1502,8 @@ var rules = [
"doubibackup.com",
"doubiyunbackup.com",
"doublethinklab.org",
- "doubmirror.cf",
"douchi.space",
"dougscripts.com",
- "douhokanko.net",
"doujincafe.com",
"dowei.org",
"dowjones.com",
@@ -1545,14 +1527,12 @@ var rules = [
"dsmtp.com",
"dssott.com",
"dstk.dk",
- "dtdns.net",
"dtiblog.com",
"dtic.mil",
"dtwang.org",
"duanzhihu.com",
"dubox.com",
"duck.com",
- "duckdns.org",
"duckduckgo.com",
"duckload.com",
"duckmylife.com",
@@ -1602,6 +1582,7 @@ var rules = [
"e-traderland.net",
"e-zone.com.hk",
"e123.hk",
+ "e621.net",
"earlytibet.com",
"earthcam.com",
"earthvpn.com",
@@ -1621,7 +1602,6 @@ var rules = [
"ebookee.com",
"ebtcbank.com",
"ecfa.org.tw",
- "echainhost.com",
"echofon.com",
"ecimg.tw",
"eckosia.org",
@@ -1635,6 +1615,7 @@ var rules = [
"edmontonservice.com",
"edns.biz",
"edoors.com",
+ "edrdg.org",
"edubridge.com",
"edupro.org",
"edx-cdn.org",
@@ -1678,8 +1659,6 @@ var rules = [
"enlighten.org.tw",
"entermap.com",
"entnt.com",
- "environment.google",
- "epa.gov.tw",
"epac.to",
"episcopalchurch.org",
"epochhk.com",
@@ -1748,7 +1727,6 @@ var rules = [
"evozi.com",
"evschool.net",
"exam.gov.tw",
- "exblog.co.jp",
"exblog.jp",
"exchristian.hk",
"excite.co.jp",
@@ -1766,7 +1744,6 @@ var rules = [
"extmatrix.com",
"extrabux.com",
"extremetube.com",
- "exx.com",
"ey.gov.tw",
"eyevio.jp",
"eyny.com",
@@ -1877,7 +1854,6 @@ var rules = [
"fc2cn.com",
"fc2web.com",
"fda.gov.tw",
- "fdbox.com",
"fdc64.de",
"fdc64.jp",
"fdc64.org",
@@ -1913,6 +1889,7 @@ var rules = [
"filmingfortibet.org",
"filthdump.com",
"financetwitter.com",
+ "financialexpress.com",
"finchvpn.com",
"findbook.tw",
"findmespot.com",
@@ -1977,7 +1954,6 @@ var rules = [
"fountmedia.io",
"fourthinternational.org",
"foxbusiness.com",
- "foxdie.us",
"foxgay.com",
"foxsub.com",
"foxtang.com",
@@ -2086,6 +2062,8 @@ var rules = [
"ftx.com",
"fucd.com",
"fuchsia.dev",
+ "fuckccp.com",
+ "fuckccp.xyz",
"fuckcnnic.net",
"fuckgfw.org",
"fuckgfw233.org",
@@ -2101,7 +2079,7 @@ var rules = [
"furbo.org",
"furhhdl.org",
"furinkan.com",
- "furl.net",
+ "furrybar.com",
"futurechinaforum.org",
"futuremessage.org",
"fux.com",
@@ -2147,6 +2125,7 @@ var rules = [
"gardennetworks.com",
"gardennetworks.org",
"gartlive.com",
+ "garudalinux.org",
"gate-project.com",
"gate.io",
"gatecoin.com",
@@ -2191,7 +2170,6 @@ var rules = [
"getchu.com",
"getcloak.com",
"getfoxyproxy.org",
- "getfreedur.com",
"getgom.com",
"geti2p.net",
"getiton.com",
@@ -2221,6 +2199,7 @@ var rules = [
"ghanely.me",
"ghidra-sre.org",
"ghostpath.com",
+ "ghproxy.com",
"ghut.org",
"giantessnight.com",
"gifree.com",
@@ -2239,6 +2218,8 @@ var rules = [
"githubassets.com",
"githubcopilot.com",
"githubusercontent.com",
+ "gitlab.com",
+ "gitlab.net",
"gizlen.net",
"gjczz.com",
"glarity.app",
@@ -2264,6 +2245,7 @@ var rules = [
"gmiddle.net",
"gmll.org",
"gmodules.com",
+ "gmp4.com",
"gmx.net",
"gnci.org.hk",
"gnews.org",
@@ -2345,6 +2327,7 @@ var rules = [
"google.cl",
"google.cm",
"google.cn",
+ "google.co",
"google.co.ao",
"google.co.bw",
"google.co.ck",
@@ -2531,7 +2514,6 @@ var rules = [
"googlehosted.com",
"googleideas.com",
"googleinsidesearch.com",
- "googlelabs.com",
"googlelocal.nl",
"googlemail.com",
"googlemaps.sv",
@@ -2540,7 +2522,6 @@ var rules = [
"googleplay.com",
"googleplus.com",
"googlescholar.com",
- "googlesile.com",
"googlesource.com",
"googlesyndication.com",
"googleusercontent.com",
@@ -2555,8 +2536,10 @@ var rules = [
"got-game.org",
"gotdns.ch",
"gotgeeks.com",
+ "gotquestions.org",
"gotrusted.com",
"gotw.ca",
+ "gov.ir",
"gov.taipei",
"gov.tw",
"gr8domain.biz",
@@ -2575,7 +2558,6 @@ var rules = [
"great-roc.org",
"greatfire.org",
"greatfirewall.biz",
- "greatfirewallofchina.net",
"greatfirewallofchina.org",
"greatroc.org",
"greatroc.tw",
@@ -2591,7 +2573,6 @@ var rules = [
"grok.com",
"grotty-monday.com",
"ground.news",
- "grow.google",
"gs-discuss.com",
"gsearch.media",
"gstatic.com",
@@ -2606,7 +2587,6 @@ var rules = [
"guancha.org",
"guaneryu.com",
"guangming.com.my",
- "guangnianvpn.com",
"guardster.com",
"guishan.org",
"gumroad.com",
@@ -2693,7 +2673,6 @@ var rules = [
"helpeachpeople.com",
"helplinfen.com",
"helpster.de",
- "helpuyghursnow.org",
"helpzhuling.org",
"henduohao.com",
"hentai.to",
@@ -2705,12 +2684,12 @@ var rules = [
"heritage.org",
"heroku.com",
"herokuapp.com",
+ "herominers.com",
"heungkongdiscuss.com",
"hexieshe.com",
"hexieshe.xyz",
"hexxeh.net",
"heyuedi.com",
- "heywire.com",
"heyzo.com",
"hgamefree.info",
"hgseav.com",
@@ -2769,7 +2748,6 @@ var rules = [
"hkcmi.edu",
"hkcnews.com",
"hkcoc.com",
- "hkctu.org.hk",
"hkdailynews.com.hk",
"hkday.net",
"hkdc.us",
@@ -2783,13 +2761,10 @@ var rules = [
"hkgalden.com",
"hkgolden.com",
"hkgpao.com",
- "hkgreenradio.org",
"hkheadline.com",
"hkhkhk.com",
"hkhrc.org.hk",
- "hkhrm.org.hk",
"hkip.org.uk",
- "hkja.org.hk",
"hkjc.com",
"hkjp.org",
"hklft.com",
@@ -2804,7 +2779,6 @@ var rules = [
"hkusu.net",
"hkvwet.com",
"hkwcc.org.hk",
- "hkzone.org",
"hmoegirl.com",
"hmonghot.com",
"hmv.co.jp",
@@ -2839,6 +2813,7 @@ var rules = [
"hotair.com",
"hotav.tv",
"hotcoin.com",
+ "hotcool.tw",
"hotels.cn",
"hotfrog.com.tw",
"hotgoo.com",
@@ -2930,7 +2905,6 @@ var rules = [
"huyandex.com",
"hwadzan.tw",
"hwayue.org.tw",
- "hwinfo.com",
"hxwk.org",
"hxwq.org",
"hybrid-analysis.com",
@@ -2952,7 +2926,6 @@ var rules = [
"iav19.com",
"iavian.net",
"ibiblio.org",
- "ibit.am",
"iblist.com",
"iblogserv-f.net",
"ibros.org",
@@ -2965,7 +2938,6 @@ var rules = [
"icij.org",
"icl-fi.org",
"icoco.com",
- "iconfactory.net",
"iconpaper.org",
"icu-project.org",
"idaiwan.com",
@@ -2973,14 +2945,13 @@ var rules = [
"identi.ca",
"idiomconnection.com",
"idlcoyote.com",
+ "idope.se",
"idouga.com",
"idreamx.com",
- "idsam.com",
"idv.tw",
"ieasy5.com",
"ied2k.net",
"ienergy1.com",
- "iepl.us",
"ifanqiang.com",
"ifcss.org",
"ifjc.org",
@@ -3055,7 +3026,6 @@ var rules = [
"illusionfactory.com",
"ilove80.be",
"ilovelongtoes.com",
- "im.tv",
"im88.tw",
"imageab.com",
"imagefap.com",
@@ -3088,6 +3058,7 @@ var rules = [
"incloak.com",
"incredibox.fr",
"independent.co.uk",
+ "india.com",
"indiablooms.com",
"indianarrative.com",
"indiandefensenews.in",
@@ -3098,7 +3069,7 @@ var rules = [
"indsr.org.tw",
"info-graf.fr",
"informer.com",
- "ingress.com",
+ "infura.io",
"inherit.live",
"initiativesforchina.org",
"inkbunny.net",
@@ -3122,7 +3093,6 @@ var rules = [
"inthenameofconfuciusmovie.com",
"invidio.us",
"inxian.com",
- "iownyour.biz",
"iownyour.org",
"ipalter.com",
"ipdefenseforum.com",
@@ -3133,6 +3103,7 @@ var rules = [
"iphonetaiwan.org",
"iphonix.fr",
"ipicture.ru",
+ "ipify.org",
"ipjetable.net",
"ipobar.com",
"ipoock.com",
@@ -3142,9 +3113,10 @@ var rules = [
"iptv.com.tw",
"iptvbin.com",
"ipvanish.com",
- "iqiyi.com",
+ "irangov.ir",
"iredmail.org",
"irib.ir",
+ "irna.ir",
"ironpython.net",
"ironsocket.com",
"is-a-hunter.com",
@@ -3182,6 +3154,7 @@ var rules = [
"itemdb.com",
"itemfix.com",
"ithome.com.tw",
+ "itiger.com",
"itsaol.com",
"itshidden.com",
"itsky.it",
@@ -3222,6 +3195,7 @@ var rules = [
"javakiba.org",
"javbus.co",
"javbus.com",
+ "javbus.sbs",
"javdb.com",
"javfinder.ai",
"javfor.me",
@@ -3236,6 +3210,7 @@ var rules = [
"javmoo.xyz",
"javseen.com",
"javtag.com",
+ "javtrailers.com",
"javzoo.com",
"javzz.com",
"jbtalks.cc",
@@ -3330,7 +3305,6 @@ var rules = [
"jwmusic.org",
"jwplayer.com",
"jyxf.net",
- "k-doujin.net",
"ka-wai.com",
"kadokawa.co.jp",
"kagyu.org",
@@ -3379,7 +3353,6 @@ var rules = [
"kichiku-doujinko.com",
"kik.com",
"killwall.com",
- "kimy.com.tw",
"kindle4rss.com",
"kindleren.com",
"kingdomsalvation.org",
@@ -3402,6 +3375,7 @@ var rules = [
"knowledgerush.com",
"knowyourmeme.com",
"ko-fi.com",
+ "kobe-np.co.jp",
"kobo.com",
"kobobooks.com",
"kodingen.com",
@@ -3416,6 +3390,7 @@ var rules = [
"kpkuang.org",
"kqes.net",
"kraken.com",
+ "krtc.com.tw",
"krtco.com.tw",
"ksdl.org",
"ksnews.com.tw",
@@ -3430,7 +3405,6 @@ var rules = [
"kurtmunger.com",
"kusocity.com",
"kwcg.ca",
- "kwok7.com",
"kwongwah.com.my",
"kxsw.life",
"kyofun.com",
@@ -3459,7 +3433,6 @@ var rules = [
"laomiu.com",
"laowang.vip",
"laoyang.info",
- "laptoplockdown.com",
"laqingdan.net",
"larsgeorge.com",
"lastcombat.com",
@@ -3469,6 +3442,8 @@ var rules = [
"lausan.hk",
"law.com",
"lbank.info",
+ "ldplayer.net",
+ "ldplayer.tw",
"le-vpn.com",
"leafyvpn.net",
"lecloud.net",
@@ -3485,15 +3460,11 @@ var rules = [
"lematin.ch",
"lemonde.fr",
"lenwhite.com",
- "leorockwell.com",
"lerosua.org",
- "lers.google",
"lesoir.be",
"lester850.info",
"letou.com",
"letscorp.net",
- "letsencrypt.org",
- "levyhsu.com",
"lflink.com",
"lflinkup.com",
"lflinkup.net",
@@ -3549,7 +3520,6 @@ var rules = [
"litenews.hk",
"lithium.com",
"liu-xiaobo.org",
- "liudejun.com",
"liuhanyu.com",
"liujianshu.com",
"liuxiaobo.net",
@@ -3569,7 +3539,6 @@ var rules = [
"lkcn.net",
"llss.me",
"lmsys.org",
- "lncn.org",
"load.to",
"lobsangwangyal.com",
"localbitcoins.com",
@@ -3608,6 +3577,8 @@ var rules = [
"lsxszzg.com",
"ltn.com.tw",
"luckydesigner.space",
+ "luckymobile.ca",
+ "ludepress.com",
"luke54.com",
"luke54.org",
"lupm.org",
@@ -3641,6 +3612,7 @@ var rules = [
"mail.ru",
"mailchimp.com",
"maildns.xyz",
+ "mainichi.jp",
"maiplus.com",
"maizhong.org",
"makemymood.com",
@@ -3689,17 +3661,17 @@ var rules = [
"matome-plus.com",
"matome-plus.net",
"matrix.org",
- "matsushimakaede.com",
"matters.news",
"matters.town",
"mattwilcox.net",
- "maturejp.com",
+ "maxai.co",
"maxing.jp",
"mayimayi.com",
"mcadforums.com",
"mcaf.ee",
"mcfog.com",
"mcreasite.com",
+ "mcusercontent.com",
"md-t.org",
"me.com",
"me.me",
@@ -3754,6 +3726,7 @@ var rules = [
"metacafe.com",
"metacubex.one",
"metafilter.com",
+ "metamask.io",
"metart.com",
"metarthunter.com",
"meteorshowersonline.com",
@@ -3770,6 +3743,7 @@ var rules = [
"mh4u.org",
"mhradio.org",
"mi.com",
+ "miami-airport.com",
"michaelmarketl.com",
"microsoft.com",
"microvpn.com",
@@ -3808,6 +3782,7 @@ var rules = [
"miniforum.org",
"miningpoolhub.com",
"ministrybooks.org",
+ "minjian-danganguan.org",
"minzhuhua.net",
"minzhuzhanxian.com",
"minzhuzhongguo.org",
@@ -3818,6 +3793,7 @@ var rules = [
"mirrormedia.com.tw",
"mirrormedia.mg",
"missav.com",
+ "missav.ws",
"mist.vip",
"mit.edu",
"mitao.com.tw",
@@ -3849,18 +3825,18 @@ var rules = [
"moeerolibrary.com",
"moegirl.org",
"moeshare.cc",
+ "moeyy.xyz",
"mofa.gov.tw",
"mofaxiehui.com",
"mofos.com",
"mog.com",
"mohu.club",
- "mohu.ml",
"mohu.rocks",
+ "moj.gov.tw",
"mojim.com",
"mol.gov.tw",
"molihua.org",
"momoshop.com.tw",
- "monar.ch",
"mondex.org",
"money-link.com.tw",
"moneydj.com",
@@ -3886,7 +3862,6 @@ var rules = [
"mos.ru",
"mosucloud.site",
"motherless.com",
- "motiyun.com",
"motor4ik.ru",
"mousebreaker.com",
"movements.org",
@@ -3912,6 +3887,7 @@ var rules = [
"msn.com.tw",
"mstdn.social",
"mswe1.org",
+ "mt.co.kr",
"mthruf.com",
"mtw.tl",
"mtzfile.pw",
@@ -3938,7 +3914,6 @@ var rules = [
"my-private-network.co.uk",
"my-proxy.com",
"my03.com",
- "my903.com",
"myactimes.com",
"myanniu.com",
"myaudiocast.com",
@@ -3964,7 +3939,6 @@ var rules = [
"myfreepaysite.com",
"myfreshnet.com",
"myftp.info",
- "myftp.name",
"myip.com",
"myiphide.com",
"myiphider.com",
@@ -3998,15 +3972,14 @@ var rules = [
"mywww.biz",
"myz.info",
"naacoalition.org",
- "nabble.com",
"naitik.net",
+ "naixi.net",
"nakido.com",
"nakuz.com",
"nalandabodhi.org",
"nalandawest.org",
"namgyal.org",
"namgyalmonastery.org",
- "namsisi.com",
"nanhuyt.com",
"nanopool.org",
"nanyang.com",
@@ -4027,6 +4000,7 @@ var rules = [
"nationsonline.org",
"nationwide.com",
"naughtyamerica.com",
+ "naver.com",
"naver.jp",
"navy.mil",
"naweeklytimes.com",
@@ -4047,12 +4021,10 @@ var rules = [
"nekoslovakia.net",
"nengcard.com",
"neo-miracle.com",
+ "neoforged.net",
"neowin.net",
- "nepusoku.com",
"nesnode.com",
- "net-fits.pro",
"netalert.me",
- "netbig.com",
"netbirds.com",
"netcolony.com",
"netfirms.com",
@@ -4075,6 +4047,7 @@ var rules = [
"newchen.com",
"newgrounds.com",
"newhighlandvision.com",
+ "newindianexpress.com",
"newipnow.com",
"newlandmagazine.com.au",
"newmitbbs.com",
@@ -4118,27 +4091,29 @@ var rules = [
"nflximg.net",
"nflxso.net",
"nflxvideo.net",
+ "nftstorage.link",
"ng.mil",
"nga.mil",
"ngensis.com",
- "ngodupdongchung.com",
"nhentai.net",
"nhi.gov.tw",
"nhk-ondemand.jp",
- "nic.google",
"nic.gov",
"nicovideo.jp",
"nighost.org",
"nightlife141.com",
"nightswatch.top",
"nike.com",
+ "nikke-en.com",
+ "nikke-jp.com",
+ "nikke-kr.com",
"nikkei.com",
"ninecommentaries.com",
"ning.com",
"ninjacloak.com",
"ninjaproxy.ninja",
"nintendium.com",
- "ninth.biz",
+ "nirsoft.net",
"nitter.cc",
"nitter.net",
"niu.moe",
@@ -4156,7 +4131,6 @@ var rules = [
"nobodycanstop.us",
"nodeseek.com",
"nodesnoop.com",
- "nofile.io",
"nokogiri.org",
"nokola.com",
"noodlevpn.com",
@@ -4168,6 +4142,7 @@ var rules = [
"nordstromrack.com",
"nordvpn.com",
"nos.nl",
+ "note.com",
"notepad-plus-plus.org",
"notion.site",
"nottinghampost.com",
@@ -4212,7 +4187,6 @@ var rules = [
"ntdtv.ru",
"ntdtvla.com",
"ntrfun.com",
- "ntsna.gov.tw",
"ntu.edu.tw",
"nu.nl",
"nubiles.net",
@@ -4225,7 +4199,6 @@ var rules = [
"nutsvpn.work",
"nuuvem.com",
"nuvid.com",
- "nuzcom.com",
"nvdst.com",
"nvquan.org",
"nvtongzhisheng.org",
@@ -4247,7 +4220,6 @@ var rules = [
"nytimes.com",
"nytimes.map.fastly.net",
"nytimg.com",
- "nytlog.com",
"nytstyle.com",
"nzchinese.com",
"nzchinese.net.nz",
@@ -4285,10 +4257,10 @@ var rules = [
"okayfreedom.com",
"okcoin.com",
"okex.com",
+ "okinawatimes.co.jp",
"okk.tw",
"okpool.me",
"okx.com",
- "olabloga.pl",
"old-cat.net",
"olehdtv.com",
"olelive.com",
@@ -4326,21 +4298,20 @@ var rules = [
"onmypc.info",
"onmypc.net",
"onmypc.org",
- "onmypc.us",
"onthehunt.com",
"ontrac.com",
- "oopsforum.com",
+ "oojj.de",
"open-assistant.io",
"open.com.hk",
"openai.com",
"openallweb.com",
"opendemocracy.net",
+ "opendesktop.org",
"opendn.xyz",
"openervpn.in",
"openid.net",
"openleaks.org",
"opensea.io",
- "opensource.google",
"openstreetmap.org",
"opentech.fund",
"openvpn.net",
@@ -4350,8 +4321,6 @@ var rules = [
"opera-mini.net",
"opera.com",
"opus-gaming.com",
- "oraclecloud.com",
- "orchidbbs.com",
"organcare.org.tw",
"organharvestinvestigation.net",
"organiccrap.com",
@@ -4361,9 +4330,9 @@ var rules = [
"orient-doll.com",
"orientaldaily.com.my",
"orn.jp",
- "orzdream.com",
"orzistic.org",
"osfoora.com",
+ "osm.tw",
"otcbtc.com",
"otnd.org",
"otto.de",
@@ -4409,6 +4378,7 @@ var rules = [
"paljorpublications.com",
"palmislife.com",
"paltalk.com",
+ "pancakeswap.finance",
"pandafan.pub",
"pandapow.co",
"pandapow.net",
@@ -4432,7 +4402,6 @@ var rules = [
"partypoker.com",
"passion.com",
"passiontimes.hk",
- "passwords.google",
"paste.ee",
"pastebin.com",
"pastie.org",
@@ -4462,7 +4431,6 @@ var rules = [
"peace.ca",
"peacefire.org",
"peacehall.com",
- "pearlher.org",
"peeasian.com",
"peing.net",
"pekingduck.org",
@@ -4485,6 +4453,7 @@ var rules = [
"perplexity.ai",
"persecutionblog.com",
"persiankitty.com",
+ "pewresearch.org",
"pfd.org.hk",
"phapluan.org",
"phayul.com",
@@ -4502,6 +4471,7 @@ var rules = [
"picacomic.com",
"picacomiccn.com",
"picasaweb.com",
+ "picgo.net",
"picidae.net",
"picturedip.com",
"picturesocial.com",
@@ -4598,6 +4568,7 @@ var rules = [
"pornhub.com",
"pornhubdeutsch.net",
"pornhubpremium.com",
+ "pornmate.com",
"pornmm.net",
"pornoxo.com",
"pornrapidshare.com",
@@ -4616,7 +4587,6 @@ var rules = [
"post76.com",
"post852.com",
"postadult.com",
- "postimg.org",
"potato.im",
"potatso.com",
"potvpn.com",
@@ -4629,20 +4599,23 @@ var rules = [
"pp.ru",
"ppy.sh",
"prayforchina.net",
+ "prcleader.org",
"premeforwindows7.com",
"premproxy.com",
"presentation.new",
"presentationzen.com",
+ "president.ir",
"presidentlee.tw",
+ "pressreader.com",
"prestige-av.com",
"prettyvirgin.com",
- "pride.google",
"primevideo.com",
"printfriendly.com",
"prism-break.org",
"prisoneralert.com",
"pritunl.com",
"privacybox.de",
+ "privacyguides.org",
"private.com",
"privateinternetaccess.com",
"privatepaste.com",
@@ -4668,7 +4641,6 @@ var rules = [
"proxydns.com",
"proxylist.org.uk",
"proxynetwork.org.uk",
- "proxypy.net",
"proxyroad.com",
"proxytunnel.net",
"proxz.com",
@@ -4702,13 +4674,13 @@ var rules = [
"pure18.com",
"pureapk.com",
"pureconcepts.net",
+ "puredns.org",
"pureinsight.org",
"purepdf.com",
"purevpn.com",
"purplelotus.org",
"pursuestar.com",
"pushchinawall.com",
- "pussthecat.org",
"pussyspace.com",
"putihome.org",
"putlocker.com",
@@ -4737,7 +4709,7 @@ var rules = [
"qiwen.lu",
"qixianglu.cn",
"qkshare.com",
- "qmzdd.com",
+ "qmp4.com",
"qoos.com",
"qpoe.com",
"qq.co.za",
@@ -4745,7 +4717,6 @@ var rules = [
"qtrac.eu",
"qtweeter.com",
"quannengshen.org",
- "quantumbooter.net",
"questvisual.com",
"quitccp.net",
"quitccp.org",
@@ -4776,10 +4747,10 @@ var rules = [
"radiotime.com",
"radiovaticana.org",
"radiovncr.com",
+ "radmin-vpn.com",
"rael.org",
"raggedbanner.com",
"raidcall.com.tw",
- "raidtalk.com.tw",
"rainbowplan.org",
"raindrop.io",
"raizoji.or.jp",
@@ -4803,11 +4774,9 @@ var rules = [
"ratx.com",
"rawgit.com",
"rawgithub.com",
- "raxcdn.com",
"razyboard.com",
"rcinet.ca",
"rd.com",
- "rdio.com",
"reabble.com",
"read01.com",
"read100.com",
@@ -4832,6 +4801,7 @@ var rules = [
"redchinacn.org",
"redd.it",
"reddit.com",
+ "reddithelp.com",
"redditlist.com",
"redditmedia.com",
"redditspace.com",
@@ -4840,7 +4810,6 @@ var rules = [
"redtube.com",
"referer.us",
"reflectivecode.com",
- "registry.google",
"reimu.net",
"relaxbbs.com",
"relay.com.tw",
@@ -4849,8 +4818,6 @@ var rules = [
"religioustolerance.org",
"renminbao.com",
"renyurenquan.org",
- "rerouted.org",
- "research.google",
"resilio.com",
"resistchina.org",
"retweeteffect.com",
@@ -4868,8 +4835,6 @@ var rules = [
"rferl.org",
"rfi.fr",
"rfi.my",
- "rightbtc.com",
- "rightster.com",
"rigpa.org",
"riku.me",
"rileyguide.com",
@@ -4900,7 +4865,6 @@ var rules = [
"rotten.com",
"rou.video",
"roucdn.link",
- "rpglogs.com",
"rsdlmonitor.com",
"rsf-chinese.org",
"rsf.org",
@@ -4917,6 +4881,7 @@ var rules = [
"ruanyifeng.com",
"rukor.org",
"rule34.xxx",
+ "rule34video.com",
"rumble.com",
"runbtx.com",
"rushbee.com",
@@ -4932,9 +4897,15 @@ var rules = [
"s-nbcnews.com",
"s1heng.com",
"s1s1s1.com",
+ "s3-ap-northeast-1.amazonaws.com",
+ "s3-ap-northeast-2.amazonaws.com",
"s3-ap-southeast-1.amazonaws.com",
"s3-ap-southeast-2.amazonaws.com",
+ "s3-eu-central-1.amazonaws.com",
"s3.amazonaws.com",
+ "s3.ap-northeast-2.amazonaws.com",
+ "s3.eu-central-1.amazonaws.com",
+ "s3.us-east-1.amazonaws.com",
"s4miniarchive.com",
"s8forum.com",
"saboom.com",
@@ -4946,10 +4917,8 @@ var rules = [
"safechat.com",
"safeguarddefenders.com",
"safervpn.com",
- "safety.google",
"sagernet.org",
"saintyculture.com",
- "saiq.me",
"sakura-paris.org",
"sakuralive.com",
"sakya.org",
@@ -4974,7 +4943,6 @@ var rules = [
"savetibetstore.org",
"saveuighur.org",
"savevid.com",
- "say2.info",
"sbme.me",
"sbs.com.au",
"scasino.com",
@@ -4982,6 +4950,7 @@ var rules = [
"sciencemag.org",
"sciencenets.com",
"scieron.com",
+ "sclub.com.tw",
"scmp.com",
"scmpchinese.com",
"scramble.io",
@@ -5023,7 +4992,6 @@ var rules = [
"sethwklein.net",
"setn.com",
"settv.com.tw",
- "setty.com.tw",
"sevenload.com",
"sex-11.com",
"sex.com",
@@ -5036,7 +5004,6 @@ var rules = [
"sexidude.com",
"sexinsex.net",
"sextvx.com",
- "sexxxy.biz",
"sf.net",
"sfileydy.com",
"sfshibao.com",
@@ -5054,7 +5021,6 @@ var rules = [
"shadowsocks.com.hk",
"shadowsocks.nu",
"shadowsocks.org",
- "shadowsocks9.com",
"shafaqna.com",
"shahit.biz",
"shambalapost.com",
@@ -5086,7 +5052,6 @@ var rules = [
"shicheng.org",
"shiksha.com",
"shiksha.ws",
- "shinychan.com",
"shipcamouflage.com",
"shireyishunjian.com",
"shitaotv.org",
@@ -5102,7 +5067,6 @@ var rules = [
"showtime.jp",
"showwe.tw",
"shutterstock.com",
- "shvoong.com",
"shwchurch.org",
"shwchurch3.com",
"siddharthasintent.org",
@@ -5119,9 +5083,9 @@ var rules = [
"simplecd.org",
"simpleproductivityblog.com",
"simpleswap.io",
+ "simplex.chat",
"sina.com",
"sina.com.hk",
- "sina.com.tw",
"sinchew.com.my",
"singaporepools.com.sg",
"singfortibet.com",
@@ -5209,7 +5173,6 @@ var rules = [
"soc.mil",
"social.edu.ci",
"socialblade.com",
- "socialwhale.com",
"socks-proxy.net",
"sockscap64.com",
"sockslist.net",
@@ -5220,9 +5183,8 @@ var rules = [
"softether.org",
"softfamous.com",
"softlayer.net",
- "softnology.biz",
"softonic.cn",
- "softsmirror.cf",
+ "softonic.com",
"softwarebychuck.com",
"sogclub.com",
"sogoo.org",
@@ -5256,7 +5218,6 @@ var rules = [
"soubory.com",
"soul-plus.net",
"soulcaliburhentai.net",
- "soumo.info",
"soundcloud.com",
"soundofhope.kr",
"soundofhope.org",
@@ -5325,6 +5286,7 @@ var rules = [
"stackoverflow.com",
"stacksocial.com",
"stage64.hk",
+ "standard.co.uk",
"standupfortibet.org",
"standwithhk.org",
"stanford.edu",
@@ -5334,6 +5296,7 @@ var rules = [
"startuplivingchina.com",
"stat.gov.tw",
"state.gov",
+ "statearmor.org",
"static-economist.com",
"statically.io",
"staticflickr.com",
@@ -5361,11 +5324,11 @@ var rules = [
"stoporganharvesting.org",
"stoptibetcrisis.net",
"storagenewsletter.com",
- "stories.google",
"storify.com",
"storj.io",
"storm.mg",
"stormmediagroup.com",
+ "storry.tv",
"stoweboyd.com",
"straitstimes.com",
"stranabg.com",
@@ -5382,9 +5345,11 @@ var rules = [
"strongwindpress.com",
"student.tw",
"studentsforafreetibet.org",
+ "studybuddhism.com",
"stumbleupon.com",
"stupidvideos.com",
"stweetly.com",
+ "subhd.tv",
"substack.com",
"successfn.com",
"sueddeutsche.de",
@@ -5392,7 +5357,6 @@ var rules = [
"sugobbs.com",
"sugumiru18.com",
"suissl.com",
- "sulian.me",
"summify.com",
"sumrando.com",
"sun1911.com",
@@ -5407,7 +5371,6 @@ var rules = [
"suoluo.org",
"supchina.com",
"superfreevpn.com",
- "superokayama.com",
"superpages.com",
"supervpn.net",
"superzooi.com",
@@ -5419,7 +5382,6 @@ var rules = [
"surfshark.com",
"suroot.com",
"surrenderat20.net",
- "sustainability.google",
"suyangg.com",
"svsfx.com",
"swagbucks.com",
@@ -5437,7 +5399,6 @@ var rules = [
"syosetu.com",
"sysresccd.org",
"sytes.net",
- "syx86.cn",
"syx86.com",
"szbbs.net",
"szetowah.org.hk",
@@ -5484,6 +5445,7 @@ var rules = [
"taiwanus.net",
"taiwanyes.com",
"talk853.com",
+ "talkatone.com",
"talkboxapp.com",
"talkcc.com",
"talkonly.net",
@@ -5492,6 +5454,7 @@ var rules = [
"tanc.org",
"tangben.com",
"tangren.us",
+ "tanks.gg",
"taoism.net",
"taolun.info",
"tapanwap.com",
@@ -5505,13 +5468,11 @@ var rules = [
"taweet.com",
"tbcollege.org",
"tbi.org.hk",
- "tbicn.org",
"tbjyt.org",
- "tbpic.info",
"tbrc.org",
"tbs-rainbow.org",
+ "tbs.co.jp",
"tbsec.org",
- "tbsmalaysia.org",
"tbsn.org",
"tbsseattle.org",
"tbssqh.org",
@@ -5544,8 +5505,8 @@ var rules = [
"teepr.com",
"tehrantimes.com",
"telecomspace.com",
+ "telega.one",
"telegra.ph",
- "telegram-cdn.org",
"telegram.dog",
"telegram.me",
"telegram.org",
@@ -5553,6 +5514,7 @@ var rules = [
"telegramdownload.com",
"telegraph.co.uk",
"telesco.pe",
+ "tellapart.com",
"tellme.pw",
"tenacy.com",
"tenor.com",
@@ -5566,6 +5528,7 @@ var rules = [
"tfhub.dev",
"tfiflve.com",
"tg-me.com",
+ "tg.dev",
"tgstat.com",
"thaicn.com",
"thb.gov.tw",
@@ -5590,11 +5553,11 @@ var rules = [
"thedw.us",
"theepochtimes.com",
"thefacebook.com",
- "thefrontier.hk",
"thegay.com",
"thegioitinhoc.vn",
"thegly.com",
"theguardian.com",
+ "thehansindia.com",
"thehindu.com",
"thehots.info",
"thehousenews.com",
@@ -5606,7 +5569,6 @@ var rules = [
"theporndude.com",
"theportalwiki.com",
"theprint.in",
- "thereallove.kr",
"therock.net.nz",
"thesaturdaypaper.com.au",
"thespeeder.com",
@@ -5620,7 +5582,6 @@ var rules = [
"thetibetpost.com",
"thetrotskymovie.com",
"thetvdb.com",
- "thevivekspot.com",
"thewgo.org",
"thewirechina.com",
"theync.com",
@@ -5634,6 +5595,7 @@ var rules = [
"thomasbernhard.org",
"thongdreams.com",
"threadreaderapp.com",
+ "threads.com",
"threads.net",
"threatchaos.com",
"throughnightsfire.com",
@@ -5738,10 +5700,12 @@ var rules = [
"ticket.com.tw",
"tigervpn.com",
"tiktok.com",
+ "tiktokcdn-eu.com",
"tiktokcdn-us.com",
"tiktokcdn.com",
"tiktokv.com",
"tiktokv.us",
+ "tiktokw.us",
"tiltbrush.com",
"timdir.com",
"time.com",
@@ -5752,7 +5716,6 @@ var rules = [
"tiney.com",
"tineye.com",
"tingtalk.me",
- "tintuc101.com",
"tiny.cc",
"tinychat.com",
"tinypaste.com",
@@ -5764,13 +5727,11 @@ var rules = [
"tl.gd",
"tma.co.jp",
"tmagazine.com",
- "tmdfish.com",
"tmi.me",
"tmpp.org",
"tnaflix.com",
- "tngrnow.com",
- "tngrnow.net",
"tnp.org",
+ "tnt-ea.com",
"to-porno.com",
"togetter.com",
"toh.info",
@@ -5783,7 +5744,6 @@ var rules = [
"tomonews.net",
"tomp3.cc",
"tongil.or.kr",
- "tono-oka.jp",
"tonyyan.net",
"toodoc.com",
"toonel.net",
@@ -5867,7 +5827,6 @@ var rules = [
"tuidang.net",
"tuidang.org",
"tuidang.se",
- "tuitui.info",
"tuitwit.com",
"tukaani.org",
"tumblr.com",
@@ -5896,11 +5855,9 @@ var rules = [
"turkistantimes.com",
"turntable.fm",
"tushycash.com",
- "tutanota.com",
"tuvpn.com",
"tuzaijidi.com",
"tv.com",
- "tv.google",
"tvants.com",
"tvb.com",
"tvboxnow.com",
@@ -5968,11 +5925,8 @@ var rules = [
"twisternow.com",
"twistory.net",
"twit2d.com",
- "twitbrowser.net",
- "twitcause.com",
"twitch.tv",
"twitchcdn.net",
- "twitgether.com",
"twitgoo.com",
"twitiq.com",
"twitlonger.com",
@@ -6009,6 +5963,7 @@ var rules = [
"twttr.com",
"twurl.nl",
"twyac.org",
+ "tx.me",
"txxx.com",
"tycool.com",
"typekit.net",
@@ -6031,6 +5986,7 @@ var rules = [
"udn.com.tw",
"udnbkk.com",
"udndata.com",
+ "udomain.hk",
"uforadio.com.tw",
"ufreevpn.com",
"ugo.com",
@@ -6077,6 +6033,7 @@ var rules = [
"untraceable.us",
"unwire.hk",
"uocn.org",
+ "upbit.com",
"updatestar.com",
"upghsbc.com",
"upholdjustice.org",
@@ -6130,7 +6087,6 @@ var rules = [
"uyghuraa.org",
"uyghuramerican.org",
"uyghurbiz.org",
- "uyghurcanadian.ca",
"uyghurcanadiansociety.org",
"uyghurcongress.org",
"uyghurensemble.co.uk",
@@ -6148,7 +6104,6 @@ var rules = [
"v2ray.com",
"v2raya.org",
"v2raycn.com",
- "v2raytech.com",
"valeursactuelles.com",
"van001.com",
"van698.com",
@@ -6203,7 +6158,6 @@ var rules = [
"virginia.edu",
"virtualrealporn.com",
"visibletweets.com",
- "visiontimes.com",
"visualstudio.com",
"vital247.org",
"viu.com",
@@ -6283,12 +6237,12 @@ var rules = [
"vpnworldwide.com",
"vporn.com",
"vpser.net",
+ "vpsxb.net",
"vraiesagesse.net",
"vrchat.com",
"vrmtr.com",
"vrporn.com",
"vrsmash.com",
- "vs.com",
"vtunnel.com",
"vuku.cc",
"vultryhw.com",
@@ -6297,6 +6251,7 @@ var rules = [
"w-pool.com",
"w.wiki",
"w3.org",
+ "w3s.link",
"waffle1999.com",
"wahas.com",
"waigaobu.com",
@@ -6304,6 +6259,7 @@ var rules = [
"wailaike.net",
"wainao.me",
"waiwaier.com",
+ "walletconnect.com",
"wallhaven.cc",
"wallmama.com",
"wallornot.org",
@@ -6323,7 +6279,6 @@ var rules = [
"want-daily.com",
"wanz-factory.com",
"wapedia.mobi",
- "warehouse333.com",
"warroom.org",
"waselpro.com",
"washeng.net",
@@ -6364,7 +6319,6 @@ var rules = [
"webworkerdaily.com",
"wechatlawsuit.com",
"weebly.com",
- "weekmag.info",
"wefightcensorship.org",
"wefong.com",
"weiboleak.com",
@@ -6380,7 +6334,6 @@ var rules = [
"wengewang.com",
"wengewang.org",
"wenhui.ch",
- "wenweipo.com",
"wenxuecity.com",
"wenyunchao.com",
"wenzhao.ca",
@@ -6395,7 +6348,6 @@ var rules = [
"wezhiyong.org",
"wezone.net",
"wforum.com",
- "wha.la",
"whatblocked.com",
"whatbrowser.org",
"whats.new",
@@ -6446,9 +6398,7 @@ var rules = [
"williamhill.com",
"willw.net",
"wilsoncenter.org",
- "windowsphoneme.com",
"windscribe.com",
- "windy.com",
"wingamestore.com",
"wingy.site",
"winning11.com",
@@ -6488,7 +6438,6 @@ var rules = [
"workerempowerment.org",
"workers.dev",
"workersthebig.net",
- "workflow.is",
"worldcat.org",
"worldjournal.com",
"worldpopulationreview.com",
@@ -6529,6 +6478,8 @@ var rules = [
"wwitv.com",
"www1.biz",
"wwwhost.biz",
+ "wxw.cat",
+ "wxw.moe",
"wzyboy.im",
"x-art.com",
"x-berry.com",
@@ -6569,6 +6520,7 @@ var rules = [
"xiaoma.org",
"xiaomi.eu",
"xiaxiaoqiang.net",
+ "xicons.org",
"xiezhua.com",
"xihua.es",
"xinbao.de",
@@ -6581,7 +6533,6 @@ var rules = [
"xinyubbs.net",
"xiongpian.com",
"xiuren.org",
- "xixicui.icu",
"xizang-zhiye.org",
"xjp.cc",
"xjtravelguide.com",
@@ -6610,7 +6561,6 @@ var rules = [
"xsden.info",
"xsden.org",
"xskywalker.com",
- "xskywalker.net",
"xt.com",
"xt.pub",
"xtube.com",
@@ -6633,7 +6583,6 @@ var rules = [
"xxx.xxx",
"xxxfuckmom.com",
"xxxx.com.au",
- "xxxy.biz",
"xxxy.info",
"xxxymovies.com",
"xys.org",
@@ -6647,6 +6596,7 @@ var rules = [
"yahoo.com.hk",
"yahoo.com.tw",
"yahoo.net",
+ "yahooinc.com",
"yahoosandbox.com",
"yakbutterblues.com",
"yam.com",
@@ -6657,6 +6607,7 @@ var rules = [
"yandex.ru",
"yanghengjun.com",
"yangjianli.com",
+ "yangzhi.org",
"yasni.co.uk",
"yasukuni.or.jp",
"yayabay.com",
@@ -6686,9 +6637,11 @@ var rules = [
"yinlei.org",
"yipub.com",
"yizhihongxing.com",
+ "ylive.jp",
"yobit.net",
"yobt.com",
"yobt.tv",
+ "yodobashi.com",
"yogichen.org",
"yolasite.com",
"yomiuri.co.jp",
@@ -6713,7 +6666,6 @@ var rules = [
"yourtrap.com",
"yousendit.com",
"youshun12.com",
- "youthforfreechina.org",
"youthnetradio.org",
"youthwant.com.tw",
"youtu.be",
@@ -6748,8 +6700,13 @@ var rules = [
"yyjlymb.xyz",
"yysub.net",
"yzzk.com",
+ "z-lib.fm",
+ "z-lib.fo",
+ "z-lib.gd",
+ "z-lib.gl",
"z-lib.io",
"z-lib.org",
+ "z-library.sk",
"zacebook.com",
"zalmos.com",
"zamimg.com",
@@ -6786,11 +6743,9 @@ var rules = [
"zhongguo.ca",
"zhongguorenquan.org",
"zhongguotese.net",
- "zhongmeng.org",
"zhongzidi.com",
"zhoushuguang.com",
"zhreader.com",
- "zhuangbi.me",
"zhuanxing.cn",
"zhuatieba.com",
"zhuichaguoji.org",
@@ -6808,6 +6763,7 @@ var rules = [
"zmedia.com.tw",
"zmw.cn",
"zodgame.us",
+ "zodgame.xyz",
"zoho.com",
"zomobo.net",
"zonaeuropa.com",
@@ -6829,11 +6785,9 @@ var rules = [
"zuobiao.me",
"zuola.com",
"zvereff.com",
- "zynaima.com",
"zynamics.com",
"zyns.com",
"zyxel.com",
- "zyzc9.com",
"zzcartoon.com",
"zzcloud.me",
"zzux.com"
From ade2db3903699ca3a29880d941b88352e185f2f1 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 28 Sep 2025 19:12:17 +0800
Subject: [PATCH 026/132] Code clean
---
v2rayN/ServiceLib/Manager/AppManager.cs | 2 --
v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs | 2 +-
v2rayN/v2rayN.Desktop/App.axaml.cs | 1 -
v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs | 2 +-
4 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs
index a0518d21..2f5ccb64 100644
--- a/v2rayN/ServiceLib/Manager/AppManager.cs
+++ b/v2rayN/ServiceLib/Manager/AppManager.cs
@@ -1,5 +1,3 @@
-using System.Reactive;
-
namespace ServiceLib.Manager;
public sealed class AppManager
diff --git a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
index ade01d3c..4c072712 100644
--- a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs
@@ -338,6 +338,6 @@ public class CheckUpdateViewModel : MyReactiveObject
{
return;
}
- found.Remarks = model.Remarks;
+ found.Remarks = model.Remarks;
}
}
diff --git a/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayN/v2rayN.Desktop/App.axaml.cs
index a3468a01..9f4d605f 100644
--- a/v2rayN/v2rayN.Desktop/App.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/App.axaml.cs
@@ -1,4 +1,3 @@
-using System.Reactive;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
diff --git a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs
index 976c156d..f59eccb1 100644
--- a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs
@@ -19,7 +19,7 @@ public partial class StatusBarView : ReactiveUserControl
InitializeComponent();
_config = AppManager.Instance.Config;
-
+
ViewModel = StatusBarViewModel.Instance;
ViewModel?.InitUpdateView(UpdateViewHandler);
From 5d6c5da9d92081d849eace39d2b9526c0655d961 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 28 Sep 2025 19:12:58 +0800
Subject: [PATCH 027/132] up 7.15.0
---
v2rayN/Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props
index c9f59177..69df0c7e 100644
--- a/v2rayN/Directory.Build.props
+++ b/v2rayN/Directory.Build.props
@@ -1,7 +1,7 @@
- 7.14.12
+ 7.15.0
From e970372a9f56b4160dbfe63c301e8a5c2b038a95 Mon Sep 17 00:00:00 2001
From: DHR60
Date: Wed, 1 Oct 2025 16:47:22 +0800
Subject: [PATCH 028/132] Fix some minor UI bugs (#8053)
---
v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 20 +++++++++----------
v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 6 +++---
v2rayN/ServiceLib/Resx/ResUI.hu.resx | 6 +++---
v2rayN/ServiceLib/Resx/ResUI.resx | 6 +++---
v2rayN/ServiceLib/Resx/ResUI.ru.resx | 6 +++---
v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 6 +++---
v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 6 +++---
.../ViewModels/DNSSettingViewModel.cs | 7 +++++++
.../Views/DNSSettingWindow.axaml | 4 +++-
.../Views/DNSSettingWindow.axaml.cs | 19 +++++++++---------
.../v2rayN.Desktop/Views/SubEditWindow.axaml | 9 +++++----
v2rayN/v2rayN/Views/DNSSettingWindow.xaml | 6 +++---
v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs | 15 +++++++-------
v2rayN/v2rayN/Views/SubEditWindow.xaml | 2 +-
14 files changed, 63 insertions(+), 55 deletions(-)
diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
index 7e848cf0..19ca4f0d 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
+++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
@@ -2913,6 +2913,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Bootstrap DNS (sing-box) 的本地化字符串。
+ ///
+ public static string TbSBBootstrapDNS {
+ get {
+ return ResourceManager.GetString("TbSBBootstrapDNS", resourceCulture);
+ }
+ }
+
///
/// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。
///
@@ -2932,16 +2941,7 @@ namespace ServiceLib.Resx {
}
///
- /// 查找类似 sing-box DoH Resolver Server 的本地化字符串。
- ///
- public static string TbSBDoHResolverServer {
- get {
- return ResourceManager.GetString("TbSBDoHResolverServer", resourceCulture);
- }
- }
-
- ///
- /// 查找类似 Fallback DNS Resolution, Suggest IP 的本地化字符串。
+ /// 查找类似 Fallback DNS Resolution, Require IP 的本地化字符串。
///
public static string TbSBFallbackDNSResolve {
get {
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
index f79901eb..7be84774 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
@@ -1425,11 +1425,11 @@
Resolve Outbound Domains
-
- sing-box DoH Resolver Server
+
+ Bootstrap DNS (sing-box)
- Fallback DNS Resolution, Suggest IP
+ Fallback DNS Resolution, Require IP
xray Freedom Resolution Strategy
diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
index e801c796..a4701f0b 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
@@ -1425,11 +1425,11 @@
Resolve Outbound Domains
-
- sing-box DoH Resolver Server
+
+ Bootstrap DNS (sing-box)
- Fallback DNS Resolution, Suggest IP
+ Fallback DNS Resolution, Require IP
xray Freedom Resolution Strategy
diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx
index fc4b218f..94e03415 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.resx
@@ -1425,11 +1425,11 @@
Resolve Outbound Domains
-
- sing-box DoH Resolver Server
+
+ Bootstrap DNS (sing-box)
- Fallback DNS Resolution, Suggest IP
+ Fallback DNS Resolution, Require IP
xray Freedom Resolution Strategy
diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
index 04e16eb8..c72c83cf 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
@@ -1425,11 +1425,11 @@
Разрешать домены для исходящих соединений
-
- Сервер DoH-резолвера (sing-box)
+
+ Bootstrap DNS (sing-box)
- Резервное DNS-разрешение (рекомендуется указывать IP)
+ Fallback DNS Resolution, Require IP
Стратегия резолвинга Freedom (Xray)
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
index ced2d74b..0d5092d0 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
@@ -1422,11 +1422,11 @@
解析出站域名
-
- sing-box DoH 解析服务器
+
+ Bootstrap DNS (sing-box)
- 兜底解析其他 DNS 域名,建议设为 ip
+ 回退 DNS 解析,需指定为 IP
xray freedom 解析策略
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
index 720b7269..4ad280b0 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
@@ -1422,11 +1422,11 @@
Resolve Outbound Domains
-
- sing-box DoH Resolver Server
+
+ Bootstrap DNS (sing-box)
- Fallback DNS Resolution, Suggest IP
+ Fallback DNS Resolution, Require IP
xray Freedom Resolution Strategy
diff --git a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs
index 9ca2d407..4f996918 100644
--- a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs
@@ -1,4 +1,5 @@
using System.Reactive;
+using System.Reactive.Linq;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
@@ -32,6 +33,8 @@ public class DNSSettingViewModel : MyReactiveObject
[Reactive] public bool RayCustomDNSEnableCompatible { get; set; }
[Reactive] public bool SBCustomDNSEnableCompatible { get; set; }
+ [ObservableAsProperty] public bool IsSimpleDNSEnabled { get; }
+
public ReactiveCommand SaveCmd { get; }
public ReactiveCommand ImportDefConfig4V2rayCompatibleCmd { get; }
public ReactiveCommand ImportDefConfig4SingboxCompatibleCmd { get; }
@@ -55,6 +58,10 @@ public class DNSSettingViewModel : MyReactiveObject
await Task.CompletedTask;
});
+ this.WhenAnyValue(x => x.RayCustomDNSEnableCompatible, x => x.SBCustomDNSEnableCompatible)
+ .Select(x => !(x.Item1 && x.Item2))
+ .ToPropertyEx(this, x => x.IsSimpleDNSEnabled);
+
_ = Init();
}
diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
index d8eed12e..cfc36aec 100644
--- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
@@ -37,6 +37,7 @@
@@ -103,7 +104,7 @@
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
- Text="{x:Static resx:ResUI.TbSBDoHResolverServer}" />
+ Text="{x:Static resx:ResUI.TbSBBootstrapDNS}" />
diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
index 2718df95..ce63afa0 100644
--- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
@@ -1,4 +1,5 @@
using System.Reactive.Disposables;
+using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Interactivity;
using ReactiveUI;
@@ -67,16 +68,14 @@ public partial class DNSSettingWindow : WindowBase
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4V2rayCompatibleCmd, v => v.btnImportDefConfig4V2rayCompatible).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCompatibleCmd, v => v.btnImportDefConfig4SingboxCompatible).DisposeWith(disposables);
- this.WhenAnyValue(
- x => x.ViewModel.RayCustomDNSEnableCompatible,
- x => x.ViewModel.SBCustomDNSEnableCompatible,
- (ray, sb) => ray && sb
- ).BindTo(this.FindControl("txtBasicDNSSettingsInvalid"), t => t.IsVisible);
- this.WhenAnyValue(
- x => x.ViewModel.RayCustomDNSEnableCompatible,
- x => x.ViewModel.SBCustomDNSEnableCompatible,
- (ray, sb) => ray && sb
- ).BindTo(this.FindControl("txtAdvancedDNSSettingsInvalid"), t => t.IsVisible);
+ this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
+ .Select(b => !b)
+ .BindTo(this.FindControl("txtBasicDNSSettingsInvalid"), t => t.IsVisible);
+ this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
+ .Select(b => !b)
+ .BindTo(this.FindControl("txtAdvancedDNSSettingsInvalid"), t => t.IsVisible);
+ this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridBasicDNSSettings.IsEnabled).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridAdvancedDNSSettings.IsEnabled).DisposeWith(disposables);
});
}
diff --git a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml
index 7ebca48f..211cff40 100644
--- a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml
@@ -75,6 +75,7 @@
Width="{StaticResource IconButtonWidth}"
Height="{StaticResource IconButtonHeight}"
Margin="{StaticResource MarginLr8}"
+ HorizontalAlignment="Left"
Theme="{DynamicResource BorderlessButton}">
@@ -208,8 +209,8 @@
Grid.Row="9"
Grid.Column="2"
Margin="{StaticResource Margin4}"
- Content="{x:Static resx:ResUI.TbSelectProfile}"
- Click="BtnSelectPrevProfile_Click" />
+ Click="BtnSelectPrevProfile_Click"
+ Content="{x:Static resx:ResUI.TbSelectProfile}" />
+ Click="BtnSelectNextProfile_Click"
+ Content="{x:Static resx:ResUI.TbSelectProfile}" />
-
+
@@ -131,7 +131,7 @@
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
- Text="{x:Static resx:ResUI.TbSBDoHResolverServer}" />
+ Text="{x:Static resx:ResUI.TbSBBootstrapDNS}" />
-
+
diff --git a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs
index b5624662..d72e98bb 100644
--- a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs
@@ -1,4 +1,5 @@
using System.Reactive.Disposables;
+using System.Reactive.Linq;
using System.Windows;
using ReactiveUI;
@@ -65,18 +66,16 @@ public partial class DNSSettingWindow
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4V2rayCompatibleCmd, v => v.btnImportDefConfig4V2rayCompatible).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCompatibleCmd, v => v.btnImportDefConfig4SingboxCompatible).DisposeWith(disposables);
- this.WhenAnyValue(
- x => x.ViewModel.RayCustomDNSEnableCompatible,
- x => x.ViewModel.SBCustomDNSEnableCompatible,
- (ray, sb) => ray && sb ? Visibility.Visible : Visibility.Collapsed)
+ this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
+ .Select(b => b ? Visibility.Collapsed : Visibility.Visible)
.BindTo(this, x => x.txtBasicDNSSettingsInvalid.Visibility)
.DisposeWith(disposables);
- this.WhenAnyValue(
- x => x.ViewModel.RayCustomDNSEnableCompatible,
- x => x.ViewModel.SBCustomDNSEnableCompatible,
- (ray, sb) => ray && sb ? Visibility.Visible : Visibility.Collapsed)
+ this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
+ .Select(b => b ? Visibility.Collapsed : Visibility.Visible)
.BindTo(this, x => x.txtAdvancedDNSSettingsInvalid.Visibility)
.DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridBasicDNSSettings.IsEnabled).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridAdvancedDNSSettings.IsEnabled).DisposeWith(disposables);
});
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
}
diff --git a/v2rayN/v2rayN/Views/SubEditWindow.xaml b/v2rayN/v2rayN/Views/SubEditWindow.xaml
index cd665cf8..62059e60 100644
--- a/v2rayN/v2rayN/Views/SubEditWindow.xaml
+++ b/v2rayN/v2rayN/Views/SubEditWindow.xaml
@@ -117,7 +117,7 @@
From 5b12c36da58aed67924ce8b18eec9860691d77b1 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 1 Oct 2025 19:49:28 +0800
Subject: [PATCH 029/132] Optimize and improve, encapsulate ProcessService
---
v2rayN/ServiceLib/Manager/CoreAdminManager.cs | 54 ++---
v2rayN/ServiceLib/Manager/CoreManager.cs | 112 ++++------
v2rayN/ServiceLib/Services/ProcessService.cs | 194 ++++++++++++++++++
.../ServiceLib/Services/SpeedtestService.cs | 22 +-
4 files changed, 255 insertions(+), 127 deletions(-)
create mode 100644 v2rayN/ServiceLib/Services/ProcessService.cs
diff --git a/v2rayN/ServiceLib/Manager/CoreAdminManager.cs b/v2rayN/ServiceLib/Manager/CoreAdminManager.cs
index 90b47106..4143e78a 100644
--- a/v2rayN/ServiceLib/Manager/CoreAdminManager.cs
+++ b/v2rayN/ServiceLib/Manager/CoreAdminManager.cs
@@ -1,4 +1,3 @@
-using System.Diagnostics;
using System.Text;
using CliWrap;
using CliWrap.Buffered;
@@ -31,7 +30,7 @@ public class CoreAdminManager
await _updateFunc?.Invoke(notify, msg);
}
- public async Task RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
+ public async Task RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
{
StringBuilder sb = new();
sb.AppendLine("#!/bin/bash");
@@ -39,50 +38,25 @@ public class CoreAdminManager
sb.AppendLine($"sudo -S {cmdLine}");
var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
- Process proc = new()
- {
- StartInfo = new()
- {
- FileName = shFilePath,
- Arguments = "",
- WorkingDirectory = Utils.GetBinConfigPath(),
- UseShellExecute = false,
- RedirectStandardInput = true,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- CreateNoWindow = true,
- StandardOutputEncoding = Encoding.UTF8,
- StandardErrorEncoding = Encoding.UTF8,
- }
- };
+ var procService = new ProcessService(
+ fileName: shFilePath,
+ arguments: "",
+ workingDirectory: Utils.GetBinConfigPath(),
+ displayLog: true,
+ redirectInput: true,
+ environmentVars: null,
+ updateFunc: _updateFunc
+ );
- void dataHandler(object sender, DataReceivedEventArgs e)
- {
- if (e.Data.IsNotEmpty())
- {
- _ = UpdateFunc(false, e.Data + Environment.NewLine);
- }
- }
+ await procService.StartAsync(AppManager.Instance.LinuxSudoPwd);
- proc.OutputDataReceived += dataHandler;
- proc.ErrorDataReceived += dataHandler;
-
- proc.Start();
- proc.BeginOutputReadLine();
- proc.BeginErrorReadLine();
-
- await Task.Delay(10);
- await proc.StandardInput.WriteLineAsync(AppManager.Instance.LinuxSudoPwd);
-
- await Task.Delay(100);
- if (proc is null or { HasExited: true })
+ if (procService is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
+ _linuxSudoPid = procService.Id;
- _linuxSudoPid = proc.Id;
-
- return proc;
+ return procService;
}
public async Task KillProcessAsLinuxSudo()
diff --git a/v2rayN/ServiceLib/Manager/CoreManager.cs b/v2rayN/ServiceLib/Manager/CoreManager.cs
index 695508c2..04f34d75 100644
--- a/v2rayN/ServiceLib/Manager/CoreManager.cs
+++ b/v2rayN/ServiceLib/Manager/CoreManager.cs
@@ -1,6 +1,3 @@
-using System.Diagnostics;
-using System.Text;
-
namespace ServiceLib.Manager;
///
@@ -11,8 +8,8 @@ public class CoreManager
private static readonly Lazy _instance = new(() => new());
public static CoreManager Instance => _instance.Value;
private Config _config;
- private Process? _process;
- private Process? _processPre;
+ private ProcessService? _processService;
+ private ProcessService? _processPreService;
private bool _linuxSudo = false;
private Func? _updateFunc;
private const string _tag = "CoreHandler";
@@ -89,13 +86,13 @@ public class CoreManager
await CoreStart(node);
await CoreStartPreService(node);
- if (_process != null)
+ if (_processService != null)
{
await UpdateFunc(true, $"{node.GetSummary()}");
}
}
- public async Task LoadCoreConfigSpeedtest(List selecteds)
+ public async Task LoadCoreConfigSpeedtest(List selecteds)
{
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) ? ECoreType.sing_box : ECoreType.Xray;
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
@@ -104,28 +101,22 @@ public class CoreManager
await UpdateFunc(false, result.Msg);
if (result.Success != true)
{
- return -1;
+ return null;
}
await UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
await UpdateFunc(false, configPath);
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
- var proc = await RunProcess(coreInfo, fileName, true, false);
- if (proc is null)
- {
- return -1;
- }
-
- return proc.Id;
+ return await RunProcess(coreInfo, fileName, true, false);
}
- public async Task LoadCoreConfigSpeedtest(ServerTestItem testItem)
+ public async Task LoadCoreConfigSpeedtest(ServerTestItem testItem)
{
var node = await AppManager.Instance.GetProfileItem(testItem.IndexId);
if (node is null)
{
- return -1;
+ return null;
}
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
@@ -133,18 +124,12 @@ public class CoreManager
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath);
if (result.Success != true)
{
- return -1;
+ return null;
}
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
- var proc = await RunProcess(coreInfo, fileName, true, false);
- if (proc is null)
- {
- return -1;
- }
-
- return proc.Id;
+ return await RunProcess(coreInfo, fileName, true, false);
}
public async Task CoreStop()
@@ -157,16 +142,18 @@ public class CoreManager
_linuxSudo = false;
}
- if (_process != null)
+ if (_processService != null)
{
- await ProcUtils.ProcessKill(_process, Utils.IsWindows());
- _process = null;
+ await _processService.StopAsync();
+ _processService.Dispose();
+ _processService = null;
}
- if (_processPre != null)
+ if (_processPreService != null)
{
- await ProcUtils.ProcessKill(_processPre, Utils.IsWindows());
- _processPre = null;
+ await _processPreService.StopAsync();
+ _processPreService.Dispose();
+ _processPreService = null;
}
}
catch (Exception ex)
@@ -188,12 +175,12 @@ public class CoreManager
{
return;
}
- _process = proc;
+ _processService = proc;
}
private async Task CoreStartPreService(ProfileItem node)
{
- if (_process != null && !_process.HasExited)
+ if (_processService != null && !_processService.HasExited)
{
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
@@ -210,7 +197,7 @@ public class CoreManager
{
return;
}
- _processPre = proc;
+ _processPreService = proc;
}
}
}
@@ -225,7 +212,7 @@ public class CoreManager
#region Process
- private async Task RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
+ private async Task RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
{
var fileName = CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out var msg);
if (fileName.IsNullOrEmpty())
@@ -256,55 +243,34 @@ public class CoreManager
}
}
- private async Task RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog)
+ private async Task RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog)
{
- Process proc = new()
- {
- StartInfo = new()
- {
- FileName = fileName,
- Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
- WorkingDirectory = Utils.GetBinConfigPath(),
- UseShellExecute = false,
- RedirectStandardOutput = displayLog,
- RedirectStandardError = displayLog,
- CreateNoWindow = true,
- StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
- StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
- }
- };
+ var environmentVars = new Dictionary();
foreach (var kv in coreInfo.Environment)
{
- proc.StartInfo.Environment[kv.Key] = string.Format(kv.Value, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath);
+ environmentVars[kv.Key] = string.Format(kv.Value, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath);
}
- if (displayLog)
- {
- void dataHandler(object sender, DataReceivedEventArgs e)
- {
- if (e.Data.IsNotEmpty())
- {
- _ = UpdateFunc(false, e.Data + Environment.NewLine);
- }
- }
- proc.OutputDataReceived += dataHandler;
- proc.ErrorDataReceived += dataHandler;
- }
- proc.Start();
+ var procService = new ProcessService(
+ fileName: fileName,
+ arguments: string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
+ workingDirectory: Utils.GetBinConfigPath(),
+ displayLog: displayLog,
+ redirectInput: false,
+ environmentVars: environmentVars,
+ updateFunc: _updateFunc
+ );
- if (displayLog)
- {
- proc.BeginOutputReadLine();
- proc.BeginErrorReadLine();
- }
+ await procService.StartAsync();
await Task.Delay(100);
- AppManager.Instance.AddProcess(proc.Handle);
- if (proc is null or { HasExited: true })
+ AppManager.Instance.AddProcess(procService.Handle);
+ if (procService is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
- return proc;
+
+ return procService;
}
#endregion Process
diff --git a/v2rayN/ServiceLib/Services/ProcessService.cs b/v2rayN/ServiceLib/Services/ProcessService.cs
new file mode 100644
index 00000000..6b964831
--- /dev/null
+++ b/v2rayN/ServiceLib/Services/ProcessService.cs
@@ -0,0 +1,194 @@
+using System.Diagnostics;
+using System.Text;
+
+namespace ServiceLib.Services;
+
+public class ProcessService : IDisposable
+{
+ private readonly Process _process;
+ private readonly Func? _updateFunc;
+ private bool _isDisposed;
+
+ public int Id => _process.Id;
+ public IntPtr Handle => _process.Handle;
+ public bool HasExited => _process.HasExited;
+
+ public ProcessService(
+ string fileName,
+ string arguments,
+ string workingDirectory,
+ bool displayLog,
+ bool redirectInput,
+ Dictionary? environmentVars,
+ Func? updateFunc)
+ {
+ _updateFunc = updateFunc;
+
+ _process = new Process
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ FileName = fileName,
+ Arguments = arguments,
+ WorkingDirectory = workingDirectory,
+ UseShellExecute = false,
+ RedirectStandardInput = redirectInput,
+ RedirectStandardOutput = displayLog,
+ RedirectStandardError = displayLog,
+ CreateNoWindow = true,
+ StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
+ StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
+ },
+ EnableRaisingEvents = true
+ };
+
+ if (environmentVars != null)
+ {
+ foreach (var kv in environmentVars)
+ {
+ _process.StartInfo.Environment[kv.Key] = kv.Value;
+ }
+ }
+
+ if (displayLog)
+ {
+ RegisterEventHandlers();
+ }
+ }
+
+ public async Task StartAsync(string pwd = null)
+ {
+ _process.Start();
+
+ if (_process.StartInfo.RedirectStandardOutput)
+ {
+ _process.BeginOutputReadLine();
+ _process.BeginErrorReadLine();
+ }
+
+ if (_process.StartInfo.RedirectStandardInput)
+ {
+ await Task.Delay(10);
+ await _process.StandardInput.WriteLineAsync(pwd);
+ }
+ }
+
+ public async Task StopAsync()
+ {
+ if (_process.HasExited)
+ {
+ return;
+ }
+
+ try
+ {
+ if (_process.StartInfo.RedirectStandardOutput)
+ {
+ try
+ {
+ _process.CancelOutputRead();
+ }
+ catch
+ {
+ throw;
+ }
+ try
+ {
+ _process.CancelErrorRead();
+ }
+ catch
+ {
+ throw;
+ }
+ }
+
+ try
+ {
+ if (Utils.IsNonWindows())
+ {
+ _process.Kill(true);
+ }
+ }
+ catch
+ {
+ throw;
+ }
+
+ try
+ {
+ _process.Kill();
+ }
+ catch
+ {
+ throw;
+ }
+
+ await Task.Delay(100);
+ }
+ catch (Exception ex)
+ {
+ await _updateFunc?.Invoke(true, ex.Message);
+ }
+ }
+
+ private void RegisterEventHandlers()
+ {
+ void dataHandler(object sender, DataReceivedEventArgs e)
+ {
+ if (e.Data.IsNotEmpty())
+ {
+ _ = _updateFunc?.Invoke(false, e.Data + Environment.NewLine);
+ }
+ }
+
+ _process.OutputDataReceived += dataHandler;
+ _process.ErrorDataReceived += dataHandler;
+
+ _process.Exited += (s, e) =>
+ {
+ try
+ {
+ _process.OutputDataReceived -= dataHandler;
+ _process.ErrorDataReceived -= dataHandler;
+ }
+ catch
+ {
+ }
+ };
+ }
+
+ public void Dispose()
+ {
+ if (_isDisposed)
+ {
+ return;
+ }
+
+ try
+ {
+ if (!_process.HasExited)
+ {
+ try
+ {
+ _process.CancelOutputRead();
+ }
+ catch { }
+ try
+ {
+ _process.CancelErrorRead();
+ }
+ catch { }
+
+ _process.Kill();
+ }
+
+ _process.Dispose();
+ }
+ catch (Exception ex)
+ {
+ _updateFunc?.Invoke(true, ex.Message);
+ }
+
+ _isDisposed = true;
+ }
+}
diff --git a/v2rayN/ServiceLib/Services/SpeedtestService.cs b/v2rayN/ServiceLib/Services/SpeedtestService.cs
index 5266f4d0..1fff0dbe 100644
--- a/v2rayN/ServiceLib/Services/SpeedtestService.cs
+++ b/v2rayN/ServiceLib/Services/SpeedtestService.cs
@@ -182,11 +182,11 @@ public class SpeedtestService(Config config, Func updateF
private async Task RunRealPingAsync(List selecteds, string exitLoopKey)
{
- var pid = -1;
+ ProcessService processService = null;
try
{
- pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(selecteds);
- if (pid < 0)
+ processService = await CoreManager.Instance.LoadCoreConfigSpeedtest(selecteds);
+ if (processService is null)
{
return false;
}
@@ -216,10 +216,7 @@ public class SpeedtestService(Config config, Func updateF
}
finally
{
- if (pid > 0)
- {
- await ProcUtils.ProcessKill(pid);
- }
+ await processService?.StopAsync();
}
return true;
}
@@ -244,11 +241,11 @@ public class SpeedtestService(Config config, Func updateF
tasks.Add(Task.Run(async () =>
{
- var pid = -1;
+ ProcessService processService = null;
try
{
- pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(it);
- if (pid < 0)
+ processService = await CoreManager.Instance.LoadCoreConfigSpeedtest(it);
+ if (processService is null)
{
await UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
}
@@ -275,10 +272,7 @@ public class SpeedtestService(Config config, Func updateF
}
finally
{
- if (pid > 0)
- {
- await ProcUtils.ProcessKill(pid);
- }
+ await processService?.StopAsync();
concurrencySemaphore.Release();
}
}));
From 12cc09d0c9994c99f00fb5f709d7d87146ae3682 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 1 Oct 2025 20:17:26 +0800
Subject: [PATCH 030/132] Bug fix
---
v2rayN/ServiceLib/Services/ProcessService.cs | 20 ++++----------------
1 file changed, 4 insertions(+), 16 deletions(-)
diff --git a/v2rayN/ServiceLib/Services/ProcessService.cs b/v2rayN/ServiceLib/Services/ProcessService.cs
index 6b964831..db3a95ac 100644
--- a/v2rayN/ServiceLib/Services/ProcessService.cs
+++ b/v2rayN/ServiceLib/Services/ProcessService.cs
@@ -88,18 +88,12 @@ public class ProcessService : IDisposable
{
_process.CancelOutputRead();
}
- catch
- {
- throw;
- }
+ catch { }
try
{
_process.CancelErrorRead();
}
- catch
- {
- throw;
- }
+ catch { }
}
try
@@ -109,19 +103,13 @@ public class ProcessService : IDisposable
_process.Kill(true);
}
}
- catch
- {
- throw;
- }
+ catch { }
try
{
_process.Kill();
}
- catch
- {
- throw;
- }
+ catch { }
await Task.Delay(100);
}
From d7c5161431c5293fb500681374857c58d5642f40 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Thu, 2 Oct 2025 19:55:49 +0800
Subject: [PATCH 031/132] Optimize and improve
---
v2rayN/ServiceLib/Common/ProcUtils.cs | 112 ------------------
.../Common/{Job.cs => WindowsJob.cs} | 6 +-
v2rayN/ServiceLib/Manager/AppManager.cs | 2 +-
v2rayN/ServiceLib/Services/ProcessService.cs | 1 +
4 files changed, 5 insertions(+), 116 deletions(-)
rename v2rayN/ServiceLib/Common/{Job.cs => WindowsJob.cs} (98%)
diff --git a/v2rayN/ServiceLib/Common/ProcUtils.cs b/v2rayN/ServiceLib/Common/ProcUtils.cs
index 561278ce..2819f2fb 100644
--- a/v2rayN/ServiceLib/Common/ProcUtils.cs
+++ b/v2rayN/ServiceLib/Common/ProcUtils.cs
@@ -67,116 +67,4 @@ public static class ProcUtils
Logging.SaveLog(_tag, ex);
}
}
-
- public static async Task ProcessKill(int pid)
- {
- try
- {
- await ProcessKill(Process.GetProcessById(pid), false);
- }
- catch (Exception ex)
- {
- Logging.SaveLog(_tag, ex);
- }
- }
-
- public static async Task ProcessKill(Process? proc, bool review)
- {
- if (proc is null)
- {
- return;
- }
-
- GetProcessKeyInfo(proc, review, out var procId, out var fileName, out var processName);
-
- try
- {
- if (Utils.IsNonWindows())
- {
- proc?.Kill(true);
- }
- }
- catch (Exception ex)
- {
- Logging.SaveLog(_tag, ex);
- }
-
- try
- {
- proc?.Kill();
- }
- catch (Exception ex)
- {
- Logging.SaveLog(_tag, ex);
- }
-
- try
- {
- proc?.Close();
- }
- catch (Exception ex)
- {
- Logging.SaveLog(_tag, ex);
- }
-
- try
- {
- proc?.Dispose();
- }
- catch (Exception ex)
- {
- Logging.SaveLog(_tag, ex);
- }
-
- await Task.Delay(300);
- await ProcessKillByKeyInfo(review, procId, fileName, processName);
- }
-
- private static void GetProcessKeyInfo(Process? proc, bool review, out int? procId, out string? fileName, out string? processName)
- {
- procId = null;
- fileName = null;
- processName = null;
- if (!review)
- {
- return;
- }
- try
- {
- procId = proc?.Id;
- fileName = proc?.MainModule?.FileName;
- processName = proc?.ProcessName;
- }
- catch (Exception ex)
- {
- Logging.SaveLog(_tag, ex);
- }
- }
-
- private static async Task ProcessKillByKeyInfo(bool review, int? procId, string? fileName, string? processName)
- {
- if (review && procId != null && fileName != null)
- {
- try
- {
- var lstProc = Process.GetProcessesByName(processName);
- foreach (var proc2 in lstProc)
- {
- if (proc2.Id == procId)
- {
- Logging.SaveLog($"{_tag}, KillProcess not completing the job, procId");
- await ProcessKill(proc2, false);
- }
- if (proc2.MainModule != null && proc2.MainModule?.FileName == fileName)
- {
- Logging.SaveLog($"{_tag}, KillProcess not completing the job, fileName");
- }
- }
- }
- catch (Exception ex)
- {
- Logging.SaveLog(_tag, ex);
- }
- }
- }
}
diff --git a/v2rayN/ServiceLib/Common/Job.cs b/v2rayN/ServiceLib/Common/WindowsJob.cs
similarity index 98%
rename from v2rayN/ServiceLib/Common/Job.cs
rename to v2rayN/ServiceLib/Common/WindowsJob.cs
index fe968d2d..f7fd2d74 100644
--- a/v2rayN/ServiceLib/Common/Job.cs
+++ b/v2rayN/ServiceLib/Common/WindowsJob.cs
@@ -7,11 +7,11 @@ namespace ServiceLib.Common;
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
*/
- public sealed class Job : IDisposable
+ public sealed class WindowsJob : IDisposable
{
private IntPtr handle = IntPtr.Zero;
- public Job()
+ public WindowsJob()
{
handle = CreateJobObject(IntPtr.Zero, null);
var extendedInfoPtr = IntPtr.Zero;
@@ -94,7 +94,7 @@ namespace ServiceLib.Common;
}
}
- ~Job()
+ ~WindowsJob()
{
Dispose(false);
}
diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs
index 2f5ccb64..16aa4253 100644
--- a/v2rayN/ServiceLib/Manager/AppManager.cs
+++ b/v2rayN/ServiceLib/Manager/AppManager.cs
@@ -8,7 +8,7 @@ public sealed class AppManager
private Config _config;
private int? _statePort;
private int? _statePort2;
- private Job? _processJob;
+ private WindowsJob? _processJob;
public static AppManager Instance => _instance.Value;
public Config Config => _config;
diff --git a/v2rayN/ServiceLib/Services/ProcessService.cs b/v2rayN/ServiceLib/Services/ProcessService.cs
index db3a95ac..06e27d52 100644
--- a/v2rayN/ServiceLib/Services/ProcessService.cs
+++ b/v2rayN/ServiceLib/Services/ProcessService.cs
@@ -178,5 +178,6 @@ public class ProcessService : IDisposable
}
_isDisposed = true;
+ GC.SuppressFinalize(this);
}
}
From 22f0d04f011e34ce174e1d4e15a2fac0ef148efd Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 3 Oct 2025 14:13:03 +0800
Subject: [PATCH 032/132] Fix
https://github.com/2dust/v2rayN/issues/8060
---
v2rayN/ServiceLib/Common/Utils.cs | 14 ++++++++++----
v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs | 2 +-
v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs | 2 +-
v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs | 2 +-
v2rayN/ServiceLib/Manager/AppManager.cs | 16 ----------------
v2rayN/ServiceLib/Manager/CoreManager.cs | 17 ++++++++++++++++-
6 files changed, 29 insertions(+), 24 deletions(-)
diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs
index 358d4e9a..85182a60 100644
--- a/v2rayN/ServiceLib/Common/Utils.cs
+++ b/v2rayN/ServiceLib/Common/Utils.cs
@@ -85,13 +85,19 @@ public class Utils
/// Base64 Encode
///
///
+ ///
///
- public static string Base64Encode(string plainText)
+ public static string Base64Encode(string plainText, bool removePadding = false)
{
try
{
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
- return Convert.ToBase64String(plainTextBytes);
+ var base64 = Convert.ToBase64String(plainTextBytes);
+ if (removePadding)
+ {
+ base64 = base64.TrimEnd('=');
+ }
+ return base64;
}
catch (Exception ex)
{
@@ -112,7 +118,7 @@ public class Utils
{
if (plainText.IsNullOrEmpty())
{
- return "";
+ return string.Empty;
}
plainText = plainText.Trim()
@@ -947,7 +953,7 @@ public class Utils
if (SetUnixFileMode(fileName))
{
Logging.SaveLog($"Successfully set the file execution permission, {fileName}");
- return "";
+ return string.Empty;
}
if (fileName.Contains(' '))
diff --git a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs
index 814d753d..decda17f 100644
--- a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs
+++ b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs
@@ -27,7 +27,7 @@ public class FmtHandler
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
- return "";
+ return string.Empty;
}
}
diff --git a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs
index 2c9898e9..2ec9769f 100644
--- a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs
+++ b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs
@@ -42,7 +42,7 @@ public class ShadowsocksFmt : BaseFmt
// item.port);
//url = Utile.Base64Encode(url);
//new Sip002
- var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
+ var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true);
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, null, remark);
}
diff --git a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs
index 6110d784..dbecdade 100644
--- a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs
+++ b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs
@@ -33,7 +33,7 @@ public class SocksFmt : BaseFmt
remark = "#" + Utils.UrlEncode(item.Remarks);
}
//new
- var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
+ var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true);
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
}
diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs
index 16aa4253..fb2a3f39 100644
--- a/v2rayN/ServiceLib/Manager/AppManager.cs
+++ b/v2rayN/ServiceLib/Manager/AppManager.cs
@@ -8,7 +8,6 @@ public sealed class AppManager
private Config _config;
private int? _statePort;
private int? _statePort2;
- private WindowsJob? _processJob;
public static AppManager Instance => _instance.Value;
public Config Config => _config;
@@ -136,21 +135,6 @@ public sealed class AppManager
return localPort + (int)protocol;
}
- public void AddProcess(nint processHandle)
- {
- if (Utils.IsWindows())
- {
- _processJob ??= new();
- try
- {
- _processJob?.AddProcess(processHandle);
- }
- catch
- {
- }
- }
- }
-
#endregion Config
#region SqliteHelper
diff --git a/v2rayN/ServiceLib/Manager/CoreManager.cs b/v2rayN/ServiceLib/Manager/CoreManager.cs
index 04f34d75..07876db8 100644
--- a/v2rayN/ServiceLib/Manager/CoreManager.cs
+++ b/v2rayN/ServiceLib/Manager/CoreManager.cs
@@ -8,6 +8,7 @@ public class CoreManager
private static readonly Lazy _instance = new(() => new());
public static CoreManager Instance => _instance.Value;
private Config _config;
+ private WindowsJob? _processJob;
private ProcessService? _processService;
private ProcessService? _processPreService;
private bool _linuxSudo = false;
@@ -264,14 +265,28 @@ public class CoreManager
await procService.StartAsync();
await Task.Delay(100);
- AppManager.Instance.AddProcess(procService.Handle);
+
if (procService is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
+ AddProcessJob(procService.Handle);
return procService;
}
+ private void AddProcessJob(nint processHandle)
+ {
+ if (Utils.IsWindows())
+ {
+ _processJob ??= new();
+ try
+ {
+ _processJob?.AddProcess(processHandle);
+ }
+ catch { }
+ }
+ }
+
#endregion Process
}
From 513662d89a4b3e4456f50aed07844bf2388c55c7 Mon Sep 17 00:00:00 2001
From: DHR60
Date: Sat, 4 Oct 2025 15:18:37 +0800
Subject: [PATCH 033/132] Use editable ComboBox instead of AutoCompleteBox
(#8067)
* Update Avalonia
* Use editable ComboBox instead of AutoCompleteBox
---
v2rayN/Directory.Packages.props | 12 ++++----
v2rayN/v2rayN.Desktop/Common/AvaUtils.cs | 7 ++---
.../Views/DNSSettingWindow.axaml | 28 +++++++++----------
.../Views/DNSSettingWindow.axaml.cs | 14 +++++-----
.../Views/OptionSettingWindow.axaml | 20 +++++++------
.../Views/RoutingRuleDetailsWindow.axaml | 16 +++++------
.../Views/RoutingRuleDetailsWindow.axaml.cs | 1 +
7 files changed, 50 insertions(+), 48 deletions(-)
diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props
index 9dcfbe61..e9d2f2b3 100644
--- a/v2rayN/Directory.Packages.props
+++ b/v2rayN/Directory.Packages.props
@@ -6,10 +6,10 @@
-
-
-
-
+
+
+
+
@@ -19,9 +19,9 @@
-
+
-
+
diff --git a/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs b/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs
index 87c974d4..f7f03ab3 100644
--- a/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs
+++ b/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs
@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
+using Avalonia.Input.Platform;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
@@ -18,7 +19,7 @@ internal class AvaUtils
return null;
}
- return await clipboard.GetTextAsync();
+ return await clipboard.TryGetTextAsync();
}
catch
{
@@ -33,9 +34,7 @@ internal class AvaUtils
var clipboard = TopLevel.GetTopLevel(visual)?.Clipboard;
if (clipboard == null)
return;
- var dataObject = new DataObject();
- dataObject.Set(DataFormats.Text, strData);
- await clipboard.SetDataObjectAsync(dataObject);
+ await clipboard.SetTextAsync(strData);
}
catch
{
diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
index cfc36aec..f8fc1ec0 100644
--- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
@@ -56,13 +56,13 @@
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbDomesticDNS}" />
-
+ IsEditable="True" />
-
+ IsEditable="True" />
-
+ IsEditable="True" />
-
+ IsEditable="True" />
-
+ IsEditable="True" />
-
+ IsEditable="True" />
@@ -435,11 +435,11 @@
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" />
-
+ IsEditable="True" />
diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
index ce63afa0..ec3ad883 100644
--- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
@@ -40,15 +40,15 @@ public partial class DNSSettingWindow : WindowBase
this.Bind(ViewModel, vm => vm.AddCommonHosts, v => v.togAddCommonHosts.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FakeIP, v => v.togFakeIP.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.IsChecked).DisposeWith(disposables);
- //this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
- //this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables);
- //this.Bind(ViewModel, vm => vm.SingboxOutboundsResolveDNS, v => v.cmbSBResolverDNS.Text).DisposeWith(disposables);
- //this.Bind(ViewModel, vm => vm.SingboxFinalResolveDNS, v => v.cmbSBFinalResolverDNS.Text).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.SingboxOutboundsResolveDNS, v => v.cmbSBResolverDNS.Text).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.SingboxFinalResolveDNS, v => v.cmbSBFinalResolverDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables);
- //this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
@@ -57,11 +57,11 @@ public partial class DNSSettingWindow : WindowBase
this.Bind(ViewModel, vm => vm.UseSystemHostsCompatible, v => v.togUseSystemHostsCompatible.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy4FreedomCompatible, v => v.cmbdomainStrategy4FreedomCompatible.SelectedItem).DisposeWith(disposables);
- //this.Bind(ViewModel, vm => vm.DomainDNSAddressCompatible, v => v.cmbdomainDNSAddressCompatible.Text).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.DomainDNSAddressCompatible, v => v.cmbdomainDNSAddressCompatible.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NormalDNSCompatible, v => v.txtnormalDNSCompatible.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom2Compatible, v => v.cmbdomainStrategy4OutCompatible.SelectedItem).DisposeWith(disposables);
- //this.Bind(ViewModel, vm => vm.DomainDNSAddress2Compatible, v => v.cmbdomainDNSAddress2Compatible.Text).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.DomainDNSAddress2Compatible, v => v.cmbdomainDNSAddress2Compatible.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NormalDNS2Compatible, v => v.txtnormalDNS2Compatible.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunDNS2Compatible, v => v.txttunDNS2Compatible.Text).DisposeWith(disposables);
diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
index 3c6e7231..e8d870bf 100644
--- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
@@ -502,12 +502,13 @@
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
-
+ Margin="{StaticResource Margin4}"
+ IsEditable="True" />
-
+ Margin="{StaticResource Margin4}"
+ IsEditable="True" />
-
+ Margin="{StaticResource Margin4}"
+ IsEditable="True" />
-
+ Margin="{StaticResource Margin4}"
+ IsEditable="True" />
-
+ IsEditable="True" />
+ VerticalAlignment="Center"
+ Orientation="Horizontal">
-
+ Click="BtnSelectProfile_Click"
+ Content="{x:Static resx:ResUI.TbSelectProfile}" />
+
{
+ this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables);
From bbe64aa97096a08a6202f4d6863ea66e11c59607 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 4 Oct 2025 16:16:32 +0800
Subject: [PATCH 034/132] Remove AutoCompleteBox
https://github.com/2dust/v2rayN/pull/8067
---
v2rayN/v2rayN.Desktop/App.axaml | 1 -
.../Controls/AutoCompleteBox.axaml | 48 -------------------
.../Controls/AutoCompleteBox.cs | 40 ----------------
.../Views/DNSSettingWindow.axaml | 1 -
.../Views/OptionSettingWindow.axaml | 10 ++--
.../Views/RoutingRuleDetailsWindow.axaml | 1 -
6 files changed, 6 insertions(+), 95 deletions(-)
delete mode 100644 v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.axaml
delete mode 100644 v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.cs
diff --git a/v2rayN/v2rayN.Desktop/App.axaml b/v2rayN/v2rayN.Desktop/App.axaml
index 222ab488..37c54471 100644
--- a/v2rayN/v2rayN.Desktop/App.axaml
+++ b/v2rayN/v2rayN.Desktop/App.axaml
@@ -20,7 +20,6 @@
-
diff --git a/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.axaml b/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.axaml
deleted file mode 100644
index 97fec908..00000000
--- a/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.axaml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.cs b/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.cs
deleted file mode 100644
index 82e04d4b..00000000
--- a/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using Avalonia.Input;
-using Avalonia.Interactivity;
-
-namespace v2rayN.Desktop.Controls;
-
-public class AutoCompleteBox : Avalonia.Controls.AutoCompleteBox
-{
- static AutoCompleteBox()
- {
- MinimumPrefixLengthProperty.OverrideDefaultValue(0);
- }
-
- public AutoCompleteBox()
- {
- AddHandler(PointerPressedEvent, OnBoxPointerPressed, RoutingStrategies.Tunnel);
- }
-
- private void OnBoxPointerPressed(object? sender, PointerPressedEventArgs e)
- {
- if (Equals(sender, this) && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
- {
- SetCurrentValue(IsDropDownOpenProperty, true);
- }
- }
-
- protected override void OnGotFocus(GotFocusEventArgs e)
- {
- base.OnGotFocus(e);
- if (IsDropDownOpen)
- {
- return;
- }
- SetCurrentValue(IsDropDownOpenProperty, true);
- }
-
- public void Clear()
- {
- SetCurrentValue(SelectedItemProperty, null);
- }
-}
diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
index f8fc1ec0..b5e0a015 100644
--- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
@@ -2,7 +2,6 @@
x:Class="v2rayN.Desktop.Views.DNSSettingWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
index e8d870bf..727f96a0 100644
--- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
@@ -2,7 +2,6 @@
x:Class="v2rayN.Desktop.Views.OptionSettingWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
@@ -622,7 +621,8 @@
Grid.Row="23"
Grid.Column="1"
Width="300"
- Margin="{StaticResource Margin4}" />
+ Margin="{StaticResource Margin4}"
+ IsEditable="True" />
+ Margin="{StaticResource Margin4}"
+ IsEditable="True" />
+ Margin="{StaticResource Margin4}"
+ IsEditable="True" />
Date: Sat, 4 Oct 2025 16:17:39 +0800
Subject: [PATCH 035/132] Fix (#8057)
---
v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 11 +------
v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 5 +--
v2rayN/ServiceLib/Resx/ResUI.hu.resx | 5 +--
v2rayN/ServiceLib/Resx/ResUI.resx | 5 +--
v2rayN/ServiceLib/Resx/ResUI.ru.resx | 5 +--
v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 5 +--
v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 5 +--
.../V2ray/CoreConfigV2rayService.cs | 2 +-
.../V2ray/V2rayConfigTemplateService.cs | 32 +++++++++++++++++--
.../Views/DNSSettingWindow.axaml | 7 ----
v2rayN/v2rayN/Views/DNSSettingWindow.xaml | 7 ----
11 files changed, 38 insertions(+), 51 deletions(-)
diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
index 19ca4f0d..eddaa0a7 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
+++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
@@ -2932,16 +2932,7 @@ namespace ServiceLib.Resx {
}
///
- /// 查找类似 The sing-box DoH resolution server can be overwritten 的本地化字符串。
- ///
- public static string TbSBDoHOverride {
- get {
- return ResourceManager.GetString("TbSBDoHOverride", resourceCulture);
- }
- }
-
- ///
- /// 查找类似 Fallback DNS Resolution, Require IP 的本地化字符串。
+ /// 查找类似 Resolve DNS server domains, requires IP 的本地化字符串。
///
public static string TbSBFallbackDNSResolve {
get {
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
index 7be84774..14b1ff6f 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
@@ -1429,7 +1429,7 @@
Bootstrap DNS (sing-box)
- Fallback DNS Resolution, Require IP
+ Resolve DNS server domains, requires IP
xray Freedom Resolution Strategy
@@ -1443,9 +1443,6 @@
Add Common DNS Hosts
-
- The sing-box DoH resolution server can be overwritten
-
FakeIP
diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
index a4701f0b..fb8c6a11 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
@@ -1429,7 +1429,7 @@
Bootstrap DNS (sing-box)
- Fallback DNS Resolution, Require IP
+ Resolve DNS server domains, requires IP
xray Freedom Resolution Strategy
@@ -1443,9 +1443,6 @@
Add Common DNS Hosts
-
- The sing-box DoH resolution server can be overwritten
-
FakeIP
diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx
index 94e03415..5b145da3 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.resx
@@ -1429,7 +1429,7 @@
Bootstrap DNS (sing-box)
- Fallback DNS Resolution, Require IP
+ Resolve DNS server domains, requires IP
xray Freedom Resolution Strategy
@@ -1443,9 +1443,6 @@
Add Common DNS Hosts
-
- The sing-box DoH resolution server can be overwritten
-
FakeIP
diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
index c72c83cf..27c2d0a3 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
@@ -1429,7 +1429,7 @@
Bootstrap DNS (sing-box)
- Fallback DNS Resolution, Require IP
+ Resolve DNS server domains, requires IP
Стратегия резолвинга Freedom (Xray)
@@ -1443,9 +1443,6 @@
Добавить стандартные записи hosts (DNS)
-
- Сервер DoH-резолвера sing-box можно переопределить
-
FakeIP
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
index 0d5092d0..a269c0a5 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
@@ -1426,7 +1426,7 @@
Bootstrap DNS (sing-box)
- 回退 DNS 解析,需指定为 IP
+ 解析 DNS 服务器域名,需指定为 IP
xray freedom 解析策略
@@ -1440,9 +1440,6 @@
添加常用 DNS Hosts
-
- 开启后可覆盖 sing-box DoH 解析服务器
-
FakeIP
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
index 4ad280b0..81a01cfc 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
@@ -1426,7 +1426,7 @@
Bootstrap DNS (sing-box)
- Fallback DNS Resolution, Require IP
+ Resolve DNS server domains, requires IP
xray Freedom Resolution Strategy
@@ -1440,9 +1440,6 @@
Add Common DNS Hosts
-
- The sing-box DoH resolution server can be overwritten
-
FakeIP
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
index b6148580..24b41109 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
@@ -186,7 +186,7 @@ public partial class CoreConfigV2rayService(Config config)
ret.Success = true;
- ret.Data = await ApplyFullConfigTemplate(v2rayConfig, true);
+ ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
return ret;
}
catch (Exception ex)
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs
index 5d1f7d63..459e77de 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs
@@ -4,7 +4,7 @@ namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigV2rayService
{
- private async Task ApplyFullConfigTemplate(V2rayConfig v2rayConfig, bool handleBalancerAndRules = false)
+ private async Task ApplyFullConfigTemplate(V2rayConfig v2rayConfig)
{
var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty())
@@ -19,7 +19,7 @@ public partial class CoreConfigV2rayService
}
// Handle balancer and rules modifications (for multiple load scenarios)
- if (handleBalancerAndRules && v2rayConfig.routing?.balancers?.Count > 0)
+ if (v2rayConfig.routing?.balancers?.Count > 0)
{
var balancer = v2rayConfig.routing.balancers.First();
@@ -60,6 +60,34 @@ public partial class CoreConfigV2rayService
}
}
+ if (v2rayConfig.observatory != null)
+ {
+ if (fullConfigTemplateNode["observatory"] == null)
+ {
+ fullConfigTemplateNode["observatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.observatory));
+ }
+ else
+ {
+ var subjectSelector = v2rayConfig.observatory.subjectSelector;
+ subjectSelector.AddRange(fullConfigTemplateNode["observatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue()) ?? []);
+ fullConfigTemplateNode["observatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
+ }
+ }
+
+ if (v2rayConfig.burstObservatory != null)
+ {
+ if (fullConfigTemplateNode["burstObservatory"] == null)
+ {
+ fullConfigTemplateNode["burstObservatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.burstObservatory));
+ }
+ else
+ {
+ var subjectSelector = v2rayConfig.burstObservatory.subjectSelector;
+ subjectSelector.AddRange(fullConfigTemplateNode["burstObservatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue()) ?? []);
+ fullConfigTemplateNode["burstObservatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
+ }
+ }
+
// Handle outbounds - append instead of override
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
foreach (var outbound in v2rayConfig.outbounds)
diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
index b5e0a015..adaaedb0 100644
--- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
@@ -173,13 +173,6 @@
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
-
diff --git a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml
index 37e2e535..cb719494 100644
--- a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml
+++ b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml
@@ -210,13 +210,6 @@
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
-
From a452bbe140c1ffa15480a859375b306a12f26971 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 4 Oct 2025 19:54:15 +0800
Subject: [PATCH 036/132] Fix
https://github.com/2dust/v2rayN/issues/8061
---
v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs | 69 +++++++++++--------
v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs | 6 +-
v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs | 2 +-
v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs | 4 +-
v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs | 8 +--
5 files changed, 49 insertions(+), 40 deletions(-)
diff --git a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs
index 965efbb1..25435fce 100644
--- a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs
+++ b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs
@@ -155,61 +155,60 @@ public class BaseFmt
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
{
- item.Flow = query["flow"] ?? "";
- item.StreamSecurity = query["security"] ?? "";
- item.Sni = query["sni"] ?? "";
- item.Alpn = Utils.UrlDecode(query["alpn"] ?? "");
- item.Fingerprint = Utils.UrlDecode(query["fp"] ?? "");
- item.PublicKey = Utils.UrlDecode(query["pbk"] ?? "");
- item.ShortId = Utils.UrlDecode(query["sid"] ?? "");
- item.SpiderX = Utils.UrlDecode(query["spx"] ?? "");
- item.Mldsa65Verify = Utils.UrlDecode(query["pqv"] ?? "");
- item.AllowInsecure = (query["allowInsecure"] ?? "") == "1" ? "true" : "";
+ item.Flow = GetQueryValue(query, "flow");
+ item.StreamSecurity = GetQueryValue(query, "security");
+ item.Sni = GetQueryValue(query, "sni");
+ item.Alpn = GetQueryDecoded(query, "alpn");
+ item.Fingerprint = GetQueryDecoded(query, "fp");
+ item.PublicKey = GetQueryDecoded(query, "pbk");
+ item.ShortId = GetQueryDecoded(query, "sid");
+ item.SpiderX = GetQueryDecoded(query, "spx");
+ item.Mldsa65Verify = GetQueryDecoded(query, "pqv");
+ item.AllowInsecure = new[] { "allowInsecure", "allow_insecure", "insecure" }.Any(k => (query[k] ?? "") == "1") ? "true" : "";
- item.Network = query["type"] ?? nameof(ETransport.tcp);
+ item.Network = GetQueryValue(query, "type", nameof(ETransport.tcp));
switch (item.Network)
{
case nameof(ETransport.tcp):
- item.HeaderType = query["headerType"] ?? Global.None;
- item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
-
+ item.HeaderType = GetQueryValue(query, "headerType", Global.None);
+ item.RequestHost = GetQueryDecoded(query, "host");
break;
case nameof(ETransport.kcp):
- item.HeaderType = query["headerType"] ?? Global.None;
- item.Path = Utils.UrlDecode(query["seed"] ?? "");
+ item.HeaderType = GetQueryValue(query, "headerType", Global.None);
+ item.Path = GetQueryDecoded(query, "seed");
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
- item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
- item.Path = Utils.UrlDecode(query["path"] ?? "/");
+ item.RequestHost = GetQueryDecoded(query, "host");
+ item.Path = GetQueryDecoded(query, "path", "/");
break;
case nameof(ETransport.xhttp):
- item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
- item.Path = Utils.UrlDecode(query["path"] ?? "/");
- item.HeaderType = Utils.UrlDecode(query["mode"] ?? "");
- item.Extra = Utils.UrlDecode(query["extra"] ?? "");
+ item.RequestHost = GetQueryDecoded(query, "host");
+ item.Path = GetQueryDecoded(query, "path", "/");
+ item.HeaderType = GetQueryDecoded(query, "mode");
+ item.Extra = GetQueryDecoded(query, "extra");
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
item.Network = nameof(ETransport.h2);
- item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
- item.Path = Utils.UrlDecode(query["path"] ?? "/");
+ item.RequestHost = GetQueryDecoded(query, "host");
+ item.Path = GetQueryDecoded(query, "path", "/");
break;
case nameof(ETransport.quic):
- item.HeaderType = query["headerType"] ?? Global.None;
- item.RequestHost = query["quicSecurity"] ?? Global.None;
- item.Path = Utils.UrlDecode(query["key"] ?? "");
+ item.HeaderType = GetQueryValue(query, "headerType", Global.None);
+ item.RequestHost = GetQueryValue(query, "quicSecurity", Global.None);
+ item.Path = GetQueryDecoded(query, "key");
break;
case nameof(ETransport.grpc):
- item.RequestHost = Utils.UrlDecode(query["authority"] ?? "");
- item.Path = Utils.UrlDecode(query["serviceName"] ?? "");
- item.HeaderType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode);
+ item.RequestHost = GetQueryDecoded(query, "authority");
+ item.Path = GetQueryDecoded(query, "serviceName");
+ item.HeaderType = GetQueryDecoded(query, "mode", Global.GrpcGunMode);
break;
default:
@@ -239,4 +238,14 @@ public class BaseFmt
var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}";
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
}
+
+ protected static string GetQueryValue(NameValueCollection query, string key, string defaultValue = "")
+ {
+ return query[key] ?? defaultValue;
+ }
+
+ protected static string GetQueryDecoded(NameValueCollection query, string key, string defaultValue = "")
+ {
+ return Utils.UrlDecode(GetQueryValue(query, key, defaultValue));
+ }
}
diff --git a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
index bc56a302..32fe81bb 100644
--- a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
+++ b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
@@ -21,10 +21,10 @@ public class Hysteria2Fmt : BaseFmt
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
- item.Path = Utils.UrlDecode(query["obfs-password"] ?? "");
- item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
+ item.Path = GetQueryDecoded(query, "obfs-password");
+ item.AllowInsecure = GetQueryValue(query, "insecure") == "1" ? "true" : "false";
- item.Ports = Utils.UrlDecode(query["mport"] ?? "");
+ item.Ports = GetQueryDecoded(query, "mport");
return item;
}
diff --git a/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs
index 632f49c9..5dcc15b1 100644
--- a/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs
+++ b/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs
@@ -30,7 +30,7 @@ public class TuicFmt : BaseFmt
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
- item.HeaderType = query["congestion_control"] ?? "";
+ item.HeaderType = GetQueryValue(query, "congestion_control");
return item;
}
diff --git a/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs
index 4d3bcff0..abf8c55a 100644
--- a/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs
+++ b/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs
@@ -24,8 +24,8 @@ public class VLESSFmt : BaseFmt
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
- item.Security = query["encryption"] ?? Global.None;
- item.StreamSecurity = query["security"] ?? "";
+ item.Security = GetQueryValue(query, "encryption", Global.None);
+ item.StreamSecurity = GetQueryValue(query, "security");
_ = ResolveStdTransport(query, ref item);
return item;
diff --git a/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs
index 8e78f036..6ceb945d 100644
--- a/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs
+++ b/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs
@@ -24,10 +24,10 @@ public class WireguardFmt : BaseFmt
var query = Utils.ParseQueryString(url.Query);
- item.PublicKey = Utils.UrlDecode(query["publickey"] ?? "");
- item.Path = Utils.UrlDecode(query["reserved"] ?? "");
- item.RequestHost = Utils.UrlDecode(query["address"] ?? "");
- item.ShortId = Utils.UrlDecode(query["mtu"] ?? "");
+ item.PublicKey = GetQueryDecoded(query, "publickey");
+ item.Path = GetQueryDecoded(query, "reserved");
+ item.RequestHost = GetQueryDecoded(query, "address");
+ item.ShortId = GetQueryDecoded(query, "mtu");
return item;
}
From 3693a7fee6302211b6902de977a13179fed0846c Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 5 Oct 2025 09:22:47 +0800
Subject: [PATCH 037/132] up 7.15.1
---
v2rayN/Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props
index 69df0c7e..792f185c 100644
--- a/v2rayN/Directory.Build.props
+++ b/v2rayN/Directory.Build.props
@@ -1,7 +1,7 @@
- 7.15.0
+ 7.15.1
From 11343a30fd432d0321ccc1e5e9fd23a32047daae Mon Sep 17 00:00:00 2001
From: DHR60
Date: Sun, 5 Oct 2025 16:27:34 +0800
Subject: [PATCH 038/132] Multi profile (#7929)
* Multi Profile
* VM and wpf
* avalonia
* Fix right click not working
* Exclude specific profile types from selection
* Rename
* Add Policy Group support
* Add generate policy group
* Adjust UI
* Add Proxy Chain support
* Fix
* Add fallback support
* Add PolicyGroup include other Group support
* Add group in traffic splitting support
* Avoid duplicate tags
* Refactor
* Adjust chained proxy, actual outbound is at the top
Based on actual network flow instead of data packets
* Add helper function
* Refactor
* Add chain selection control to group outbounds
* Avoid self-reference
* Fix
* Improves Tun2Socks address handling
* Avoids circular dependency in profile groups
Adds cycle detection to prevent infinite loops when evaluating profile groups.
This ensures that profile group configurations don't result in stack overflow errors when groups reference each other, directly or indirectly.
* Fix
* Fix
* Update ProfileGroupItem.cs
* Refactor
* Remove unnecessary checks
---------
Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
---
v2rayN/ServiceLib/Enums/EConfigType.cs | 6 +-
v2rayN/ServiceLib/Enums/EMultipleLoad.cs | 3 +-
v2rayN/ServiceLib/Enums/EViewAction.cs | 1 +
v2rayN/ServiceLib/Global.cs | 1 +
v2rayN/ServiceLib/Handler/ConfigHandler.cs | 114 ++++++--
.../ServiceLib/Handler/CoreConfigHandler.cs | 20 --
v2rayN/ServiceLib/Manager/AppManager.cs | 10 +
.../Manager/ProfileGroupItemManager.cs | 276 ++++++++++++++++++
v2rayN/ServiceLib/Models/ProfileGroupItem.cs | 14 +
v2rayN/ServiceLib/Models/ProfileItem.cs | 68 ++++-
v2rayN/ServiceLib/Models/SingboxConfig.cs | 1 +
v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 252 ++++++++++++----
v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 62 +++-
v2rayN/ServiceLib/Resx/ResUI.hu.resx | 62 +++-
v2rayN/ServiceLib/Resx/ResUI.resx | 62 +++-
v2rayN/ServiceLib/Resx/ResUI.ru.resx | 62 +++-
v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 62 +++-
v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 62 +++-
.../Singbox/CoreConfigSingboxService.cs | 149 +++++-----
.../CoreConfig/Singbox/SingboxDnsService.cs | 15 +-
.../Singbox/SingboxOutboundService.cs | 267 ++++++++++++++++-
.../Singbox/SingboxRoutingService.cs | 18 +-
.../V2ray/CoreConfigV2rayService.cs | 187 +++++++-----
.../CoreConfig/V2ray/V2rayBalancerService.cs | 112 +++++--
.../CoreConfig/V2ray/V2rayOutboundService.cs | 197 ++++++++++++-
.../CoreConfig/V2ray/V2rayRoutingService.cs | 16 +-
.../ViewModels/AddGroupServerViewModel.cs | 225 ++++++++++++++
.../ViewModels/MainWindowViewModel.cs | 15 +
.../ViewModels/ProfilesViewModel.cs | 48 +--
.../Views/AddGroupServerWindow.axaml | 151 ++++++++++
.../Views/AddGroupServerWindow.axaml.cs | 166 +++++++++++
v2rayN/v2rayN.Desktop/Views/MainWindow.axaml | 2 +
.../v2rayN.Desktop/Views/MainWindow.axaml.cs | 7 +
.../v2rayN.Desktop/Views/ProfilesView.axaml | 13 +-
.../Views/ProfilesView.axaml.cs | 16 +-
.../Views/SubEditWindow.axaml.cs | 4 +-
v2rayN/v2rayN/Views/AddGroupServerWindow.xaml | 213 ++++++++++++++
.../v2rayN/Views/AddGroupServerWindow.xaml.cs | 148 ++++++++++
v2rayN/v2rayN/Views/MainWindow.xaml | 8 +
v2rayN/v2rayN/Views/MainWindow.xaml.cs | 7 +
v2rayN/v2rayN/Views/ProfilesView.xaml | 26 +-
v2rayN/v2rayN/Views/ProfilesView.xaml.cs | 16 +-
v2rayN/v2rayN/Views/SubEditWindow.xaml.cs | 4 +-
43 files changed, 2756 insertions(+), 412 deletions(-)
create mode 100644 v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs
create mode 100644 v2rayN/ServiceLib/Models/ProfileGroupItem.cs
create mode 100644 v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs
create mode 100644 v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml
create mode 100644 v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs
create mode 100644 v2rayN/v2rayN/Views/AddGroupServerWindow.xaml
create mode 100644 v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs
diff --git a/v2rayN/ServiceLib/Enums/EConfigType.cs b/v2rayN/ServiceLib/Enums/EConfigType.cs
index 6698f962..5ddb8451 100644
--- a/v2rayN/ServiceLib/Enums/EConfigType.cs
+++ b/v2rayN/ServiceLib/Enums/EConfigType.cs
@@ -12,5 +12,9 @@ public enum EConfigType
TUIC = 8,
WireGuard = 9,
HTTP = 10,
- Anytls = 11
+ Anytls = 11,
+
+ Group = 1000,
+ PolicyGroup = 1001,
+ ProxyChain = 1002,
}
diff --git a/v2rayN/ServiceLib/Enums/EMultipleLoad.cs b/v2rayN/ServiceLib/Enums/EMultipleLoad.cs
index 42be7a5b..ef6a5ff9 100644
--- a/v2rayN/ServiceLib/Enums/EMultipleLoad.cs
+++ b/v2rayN/ServiceLib/Enums/EMultipleLoad.cs
@@ -2,8 +2,9 @@ namespace ServiceLib.Enums;
public enum EMultipleLoad
{
+ LeastPing,
+ Fallback,
Random,
RoundRobin,
- LeastPing,
LeastLoad
}
diff --git a/v2rayN/ServiceLib/Enums/EViewAction.cs b/v2rayN/ServiceLib/Enums/EViewAction.cs
index 075439fd..fba340dc 100644
--- a/v2rayN/ServiceLib/Enums/EViewAction.cs
+++ b/v2rayN/ServiceLib/Enums/EViewAction.cs
@@ -23,6 +23,7 @@ public enum EViewAction
RoutingRuleDetailsWindow,
AddServerWindow,
AddServer2Window,
+ AddGroupServerWindow,
DNSSettingWindow,
RoutingSettingWindow,
OptionSettingWindow,
diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs
index 6c4a4362..c7f18d2c 100644
--- a/v2rayN/ServiceLib/Global.cs
+++ b/v2rayN/ServiceLib/Global.cs
@@ -50,6 +50,7 @@ public class Global
public const string DirectTag = "direct";
public const string BlockTag = "block";
public const string DnsTag = "dns-module";
+ public const string BalancerTagSuffix = "-round";
public const string StreamSecurity = "tls";
public const string StreamSecurityReality = "reality";
public const string Loopback = "127.0.0.1";
diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
index 65f2ee53..2c6428d6 100644
--- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs
+++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
@@ -357,6 +357,11 @@ public static class ConfigHandler
{
}
}
+ else if (profileItem.ConfigType > EConfigType.Group)
+ {
+ var profileGroupItem = await AppManager.Instance.GetProfileGroupItem(it.IndexId);
+ await AddGroupServerCommon(config, profileItem, profileGroupItem, true);
+ }
else
{
await AddServerCommon(config, profileItem, true);
@@ -1074,6 +1079,37 @@ public static class ConfigHandler
return 0;
}
+ public static async Task AddGroupServerCommon(Config config, ProfileItem profileItem, ProfileGroupItem profileGroupItem, bool toFile = true)
+ {
+ var maxSort = -1;
+ if (profileItem.IndexId.IsNullOrEmpty())
+ {
+ profileItem.IndexId = Utils.GetGuid(false);
+ maxSort = ProfileExManager.Instance.GetMaxSort();
+ }
+ var groupType = profileItem.ConfigType == EConfigType.ProxyChain ? EConfigType.ProxyChain.ToString() : profileGroupItem.MultipleLoad.ToString();
+ profileItem.Address = $"{profileItem.CoreType}-{groupType}";
+ if (maxSort > 0)
+ {
+ ProfileExManager.Instance.SetSort(profileItem.IndexId, maxSort + 1);
+ }
+ if (toFile)
+ {
+ await SQLiteHelper.Instance.ReplaceAsync(profileItem);
+ if (profileGroupItem != null)
+ {
+ profileGroupItem.ParentIndexId = profileItem.IndexId;
+ await ProfileGroupItemManager.Instance.SaveItemAsync(profileGroupItem);
+ }
+ else
+ {
+ ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(profileItem.IndexId);
+ await ProfileGroupItemManager.Instance.SaveTo();
+ }
+ }
+ return 0;
+ }
+
///
/// Compare two profile items to determine if they represent the same server
/// Used for deduplication and server matching
@@ -1145,7 +1181,7 @@ public static class ConfigHandler
}
///
- /// Create a custom server that combines multiple servers for load balancing
+ /// Create a group server that combines multiple servers for load balancing
/// Generates a configuration file that references multiple servers
///
/// Current configuration
@@ -1153,45 +1189,54 @@ public static class ConfigHandler
/// Core type to use (Xray or sing_box)
/// Load balancing algorithm
/// Result object with success state and data
- public static async Task AddCustomServer4Multiple(Config config, List selecteds, ECoreType coreType, EMultipleLoad multipleLoad)
+ public static async Task AddGroupServer4Multiple(Config config, List selecteds, ECoreType coreType, EMultipleLoad multipleLoad, string? subId)
{
- var indexId = Utils.GetMd5(Global.CoreMultipleLoadConfigFileName);
- var configPath = Utils.GetConfigPath(Global.CoreMultipleLoadConfigFileName);
+ var result = new RetResult();
- var result = await CoreConfigHandler.GenerateClientMultipleLoadConfig(config, configPath, selecteds, coreType, multipleLoad);
- if (result.Success != true)
- {
- return result;
- }
+ var indexId = Utils.GetGuid(false);
+ var childProfileIndexId = Utils.List2String(selecteds.Select(p => p.IndexId).ToList());
- if (!File.Exists(configPath))
- {
- return result;
- }
-
- var profileItem = await AppManager.Instance.GetProfileItem(indexId) ?? new();
- profileItem.IndexId = indexId;
+ var remark = subId.IsNullOrEmpty() ? string.Empty : $"{(await AppManager.Instance.GetSubItem(subId)).Remarks} ";
if (coreType == ECoreType.Xray)
{
- profileItem.Remarks = multipleLoad switch
+ remark += multipleLoad switch
{
- EMultipleLoad.Random => ResUI.menuSetDefaultMultipleServerXrayRandom,
- EMultipleLoad.RoundRobin => ResUI.menuSetDefaultMultipleServerXrayRoundRobin,
- EMultipleLoad.LeastPing => ResUI.menuSetDefaultMultipleServerXrayLeastPing,
- EMultipleLoad.LeastLoad => ResUI.menuSetDefaultMultipleServerXrayLeastLoad,
- _ => ResUI.menuSetDefaultMultipleServerXrayRoundRobin,
+ EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerXrayLeastPing,
+ EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerXrayFallback,
+ EMultipleLoad.Random => ResUI.menuGenGroupMultipleServerXrayRandom,
+ EMultipleLoad.RoundRobin => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
+ EMultipleLoad.LeastLoad => ResUI.menuGenGroupMultipleServerXrayLeastLoad,
+ _ => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
};
}
else if (coreType == ECoreType.sing_box)
{
- profileItem.Remarks = ResUI.menuSetDefaultMultipleServerSingBoxLeastPing;
+ remark += multipleLoad switch
+ {
+ EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
+ EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerSingBoxFallback,
+ _ => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
+ };
}
- profileItem.Address = Global.CoreMultipleLoadConfigFileName;
- profileItem.ConfigType = EConfigType.Custom;
- profileItem.CoreType = coreType;
-
- await AddServerCommon(config, profileItem, true);
-
+ var profile = new ProfileItem
+ {
+ IndexId = indexId,
+ CoreType = coreType,
+ ConfigType = EConfigType.PolicyGroup,
+ Remarks = remark,
+ };
+ if (!subId.IsNullOrEmpty())
+ {
+ profile.Subid = subId;
+ }
+ var profileGroup = new ProfileGroupItem
+ {
+ ChildItems = childProfileIndexId,
+ MultipleLoad = multipleLoad,
+ ParentIndexId = indexId,
+ };
+ var ret = await AddGroupServerCommon(config, profile, profileGroup, true);
+ result.Success = ret == 0;
result.Data = indexId;
return result;
}
@@ -1209,12 +1254,21 @@ public static class ConfigHandler
ProfileItem? itemSocks = null;
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
{
+ var tun2SocksAddress = node.Address;
+ if (node.ConfigType > EConfigType.Group)
+ {
+ var lstAddresses = (await ProfileGroupItemManager.GetAllChildDomainAddresses(node.IndexId)).ToList();
+ if (lstAddresses.Count > 0)
+ {
+ tun2SocksAddress = Utils.List2String(lstAddresses);
+ }
+ }
itemSocks = new ProfileItem()
{
CoreType = ECoreType.sing_box,
ConfigType = EConfigType.SOCKS,
Address = Global.Loopback,
- SpiderX = node.Address, // Tun2SocksAddress
+ SpiderX = tun2SocksAddress, // Tun2SocksAddress
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
};
}
diff --git a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs
index 66d261ac..96c72bb1 100644
--- a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs
+++ b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs
@@ -132,24 +132,4 @@ public static class CoreConfigHandler
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result;
}
-
- public static async Task GenerateClientMultipleLoadConfig(Config config, string fileName, List selecteds, ECoreType coreType, EMultipleLoad multipleLoad)
- {
- var result = new RetResult();
- if (coreType == ECoreType.sing_box)
- {
- result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds);
- }
- else
- {
- result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad);
- }
-
- if (result.Success != true)
- {
- return result;
- }
- await File.WriteAllTextAsync(fileName, result.Data.ToString());
- return result;
- }
}
diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs
index fb2a3f39..08e96857 100644
--- a/v2rayN/ServiceLib/Manager/AppManager.cs
+++ b/v2rayN/ServiceLib/Manager/AppManager.cs
@@ -64,6 +64,7 @@ public sealed class AppManager
SQLiteHelper.Instance.CreateTable();
SQLiteHelper.Instance.CreateTable();
SQLiteHelper.Instance.CreateTable();
+ SQLiteHelper.Instance.CreateTable();
return true;
}
@@ -207,6 +208,15 @@ public sealed class AppManager
return await SQLiteHelper.Instance.TableAsync().FirstOrDefaultAsync(it => it.Remarks == remarks);
}
+ public async Task GetProfileGroupItem(string parentIndexId)
+ {
+ if (parentIndexId.IsNullOrEmpty())
+ {
+ return null;
+ }
+ return await SQLiteHelper.Instance.TableAsync().FirstOrDefaultAsync(it => it.ParentIndexId == parentIndexId);
+ }
+
public async Task?> RoutingItems()
{
return await SQLiteHelper.Instance.TableAsync().OrderBy(t => t.Sort).ToListAsync();
diff --git a/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs b/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs
new file mode 100644
index 00000000..447e7433
--- /dev/null
+++ b/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs
@@ -0,0 +1,276 @@
+using System.Collections.Concurrent;
+
+namespace ServiceLib.Manager;
+
+public class ProfileGroupItemManager
+{
+ private static readonly Lazy _instance = new(() => new());
+ private ConcurrentDictionary _items = new();
+
+ public static ProfileGroupItemManager Instance => _instance.Value;
+ private static readonly string _tag = "ProfileGroupItemManager";
+
+ private ProfileGroupItemManager()
+ {
+ }
+
+ public async Task Init()
+ {
+ await InitData();
+ }
+
+ // Read-only getters: do not create or mark dirty
+ public bool TryGet(string indexId, out ProfileGroupItem? item)
+ {
+ item = null;
+ if (string.IsNullOrWhiteSpace(indexId))
+ {
+ return false;
+ }
+
+ return _items.TryGetValue(indexId, out item);
+ }
+
+ public ProfileGroupItem? GetOrDefault(string indexId)
+ {
+ return string.IsNullOrWhiteSpace(indexId) ? null : (_items.TryGetValue(indexId, out var v) ? v : null);
+ }
+
+ private async Task InitData()
+ {
+ await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem where parentIndexId not in ( select indexId from ProfileItem )");
+
+ var list = await SQLiteHelper.Instance.TableAsync().ToListAsync();
+ _items = new ConcurrentDictionary(list.Where(t => !string.IsNullOrEmpty(t.ParentIndexId)).ToDictionary(t => t.ParentIndexId!));
+ }
+
+ private ProfileGroupItem AddProfileGroupItem(string indexId)
+ {
+ var profileGroupItem = new ProfileGroupItem()
+ {
+ ParentIndexId = indexId,
+ ChildItems = string.Empty,
+ MultipleLoad = EMultipleLoad.LeastPing
+ };
+
+ _items[indexId] = profileGroupItem;
+ return profileGroupItem;
+ }
+
+ private ProfileGroupItem GetProfileGroupItem(string indexId)
+ {
+ if (string.IsNullOrEmpty(indexId))
+ {
+ indexId = Utils.GetGuid(false);
+ }
+
+ return _items.GetOrAdd(indexId, AddProfileGroupItem);
+ }
+
+ public async Task ClearAll()
+ {
+ await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem ");
+ _items.Clear();
+ }
+
+ public async Task SaveTo()
+ {
+ try
+ {
+ var lstExists = await SQLiteHelper.Instance.TableAsync().ToListAsync();
+ var existsMap = lstExists.Where(t => !string.IsNullOrEmpty(t.ParentIndexId)).ToDictionary(t => t.ParentIndexId!);
+
+ var lstInserts = new List();
+ var lstUpdates = new List();
+
+ foreach (var item in _items.Values)
+ {
+ if (string.IsNullOrEmpty(item.ParentIndexId))
+ {
+ continue;
+ }
+
+ if (existsMap.ContainsKey(item.ParentIndexId))
+ {
+ lstUpdates.Add(item);
+ }
+ else
+ {
+ lstInserts.Add(item);
+ }
+ }
+
+ try
+ {
+ if (lstInserts.Count > 0)
+ {
+ await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
+ }
+
+ if (lstUpdates.Count > 0)
+ {
+ await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
+ }
+ }
+ catch (Exception ex)
+ {
+ Logging.SaveLog(_tag, ex);
+ }
+ }
+ catch (Exception ex)
+ {
+ Logging.SaveLog(_tag, ex);
+ }
+ }
+
+ public ProfileGroupItem GetOrCreateAndMarkDirty(string indexId)
+ {
+ return GetProfileGroupItem(indexId);
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ await SaveTo();
+ }
+
+ public async Task SaveItemAsync(ProfileGroupItem item)
+ {
+ if (item is null)
+ {
+ throw new ArgumentNullException(nameof(item));
+ }
+
+ if (string.IsNullOrWhiteSpace(item.ParentIndexId))
+ {
+ throw new ArgumentException("ParentIndexId required", nameof(item));
+ }
+
+ _items[item.ParentIndexId] = item;
+
+ try
+ {
+ var lst = await SQLiteHelper.Instance.TableAsync().Where(t => t.ParentIndexId == item.ParentIndexId).ToListAsync();
+ if (lst != null && lst.Count > 0)
+ {
+ await SQLiteHelper.Instance.UpdateAllAsync(new List { item });
+ }
+ else
+ {
+ await SQLiteHelper.Instance.InsertAllAsync(new List { item });
+ }
+ }
+ catch (Exception ex)
+ {
+ Logging.SaveLog(_tag, ex);
+ }
+ }
+
+ #region Helper
+
+ public static bool HasCycle(string? indexId)
+ {
+ return HasCycle(indexId, new HashSet(), new HashSet());
+ }
+
+ public static bool HasCycle(string? indexId, HashSet visited, HashSet stack)
+ {
+ if (indexId.IsNullOrEmpty())
+ return false;
+
+ if (stack.Contains(indexId))
+ return true;
+
+ if (visited.Contains(indexId))
+ return false;
+
+ visited.Add(indexId);
+ stack.Add(indexId);
+
+ Instance.TryGet(indexId, out var groupItem);
+
+ if (groupItem == null || groupItem.ChildItems.IsNullOrEmpty())
+ {
+ return false;
+ }
+
+ var childIds = Utils.String2List(groupItem.ChildItems)
+ .Where(p => !string.IsNullOrEmpty(p))
+ .ToList();
+
+ foreach (var child in childIds)
+ {
+ if (HasCycle(child, visited, stack))
+ {
+ return true;
+ }
+ }
+
+ stack.Remove(indexId);
+ return false;
+ }
+
+ public static async Task<(List Items, ProfileGroupItem? Group)> GetChildProfileItems(string? indexId)
+ {
+ Instance.TryGet(indexId, out var profileGroupItem);
+ if (profileGroupItem == null || profileGroupItem.ChildItems.IsNullOrEmpty())
+ {
+ return (new List(), profileGroupItem);
+ }
+ var items = await GetChildProfileItems(profileGroupItem);
+ return (items, profileGroupItem);
+ }
+
+ public static async Task> GetChildProfileItems(ProfileGroupItem? group)
+ {
+ if (group == null || group.ChildItems.IsNullOrEmpty())
+ {
+ return new();
+ }
+ var childProfiles = (await Task.WhenAll(
+ Utils.String2List(group.ChildItems)
+ .Where(p => !p.IsNullOrEmpty())
+ .Select(AppManager.Instance.GetProfileItem)
+ ))
+ .Where(p =>
+ p != null &&
+ p.IsValid() &&
+ p.ConfigType != EConfigType.Custom
+ )
+ .ToList();
+ return childProfiles;
+ }
+
+ public static async Task> GetAllChildDomainAddresses(string parentIndexId)
+ {
+ // include grand children
+ var childAddresses = new HashSet();
+ if (!Instance.TryGet(parentIndexId, out var groupItem) || groupItem.ChildItems.IsNullOrEmpty())
+ return childAddresses;
+
+ var childIds = Utils.String2List(groupItem.ChildItems);
+
+ foreach (var childId in childIds)
+ {
+ var childNode = await AppManager.Instance.GetProfileItem(childId);
+ if (childNode == null)
+ continue;
+
+ if (!childNode.IsComplex())
+ {
+ childAddresses.Add(childNode.Address);
+ }
+ else if (childNode.ConfigType > EConfigType.Group)
+ {
+ var subAddresses = await GetAllChildDomainAddresses(childNode.IndexId);
+ foreach (var addr in subAddresses)
+ {
+ childAddresses.Add(addr);
+ }
+ }
+ }
+
+ return childAddresses;
+ }
+
+ #endregion Helper
+}
diff --git a/v2rayN/ServiceLib/Models/ProfileGroupItem.cs b/v2rayN/ServiceLib/Models/ProfileGroupItem.cs
new file mode 100644
index 00000000..6fbe1d9c
--- /dev/null
+++ b/v2rayN/ServiceLib/Models/ProfileGroupItem.cs
@@ -0,0 +1,14 @@
+using SQLite;
+
+namespace ServiceLib.Models;
+
+[Serializable]
+public class ProfileGroupItem
+{
+ [PrimaryKey]
+ public string ParentIndexId { get; set; }
+
+ public string ChildItems { get; set; }
+
+ public EMultipleLoad MultipleLoad { get; set; } = EMultipleLoad.LeastPing;
+}
diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs
index 998aa120..da34600b 100644
--- a/v2rayN/ServiceLib/Models/ProfileItem.cs
+++ b/v2rayN/ServiceLib/Models/ProfileItem.cs
@@ -32,18 +32,21 @@ public class ProfileItem : ReactiveObject
public string GetSummary()
{
var summary = $"[{(ConfigType).ToString()}] ";
- var arrAddr = Address.Contains(':') ? Address.Split(':') : Address.Split('.');
- var addr = arrAddr.Length switch
+ if (IsComplex())
{
- > 2 => $"{arrAddr.First()}***{arrAddr.Last()}",
- > 1 => $"***{arrAddr.Last()}",
- _ => Address
- };
- summary += ConfigType switch
+ summary += $"[{CoreType.ToString()}]{Remarks}";
+ }
+ else
{
- EConfigType.Custom => $"[{CoreType.ToString()}]{Remarks}",
- _ => $"{Remarks}({addr}:{Port})"
- };
+ var arrAddr = Address.Contains(':') ? Address.Split(':') : Address.Split('.');
+ var addr = arrAddr.Length switch
+ {
+ > 2 => $"{arrAddr.First()}***{arrAddr.Last()}",
+ > 1 => $"***{arrAddr.Last()}",
+ _ => Address
+ };
+ summary += $"{Remarks}({addr}:{Port})";
+ }
return summary;
}
@@ -61,6 +64,51 @@ public class ProfileItem : ReactiveObject
return Network.TrimEx();
}
+ public bool IsComplex()
+ {
+ return ConfigType is EConfigType.Custom or > EConfigType.Group;
+ }
+
+ public bool IsValid()
+ {
+ if (IsComplex())
+ return true;
+
+ if (Address.IsNullOrEmpty() || Port is <= 0 or >= 65536)
+ return false;
+
+ switch (ConfigType)
+ {
+ case EConfigType.VMess:
+ if (Id.IsNullOrEmpty() || !Utils.IsGuidByParse(Id))
+ return false;
+ break;
+
+ case EConfigType.VLESS:
+ if (Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(Id) && Id.Length > 30))
+ return false;
+ if (!Global.Flows.Contains(Flow))
+ return false;
+ break;
+
+ case EConfigType.Shadowsocks:
+ if (Id.IsNullOrEmpty())
+ return false;
+ if (string.IsNullOrEmpty(Security) || !Global.SsSecuritiesInSingbox.Contains(Security))
+ return false;
+ break;
+ }
+
+ if ((ConfigType is EConfigType.VLESS or EConfigType.Trojan)
+ && StreamSecurity == Global.StreamSecurityReality
+ && PublicKey.IsNullOrEmpty())
+ {
+ return false;
+ }
+
+ return true;
+ }
+
#endregion function
[PrimaryKey]
diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs
index a5eec4ae..8263924a 100644
--- a/v2rayN/ServiceLib/Models/SingboxConfig.cs
+++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs
@@ -145,6 +145,7 @@ public class Outbound4Sbox : BaseServer4Sbox
public string? plugin_opts { get; set; }
public List? outbounds { get; set; }
public bool? interrupt_exist_connections { get; set; }
+ public int? tolerance { get; set; }
}
public class Endpoints4Sbox : BaseServer4Sbox
diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
index eddaa0a7..088f9b96 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
+++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
@@ -672,6 +672,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Add Child Configuration 的本地化字符串。
+ ///
+ public static string menuAddChildServer {
+ get {
+ return ResourceManager.GetString("menuAddChildServer", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Add a custom configuration Configuration 的本地化字符串。
///
@@ -699,6 +708,24 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Add Policy Group Configuration 的本地化字符串。
+ ///
+ public static string menuAddPolicyGroupServer {
+ get {
+ return ResourceManager.GetString("menuAddPolicyGroupServer", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Add Proxy Chain Configuration 的本地化字符串。
+ ///
+ public static string menuAddProxyChainServer {
+ get {
+ return ResourceManager.GetString("menuAddProxyChainServer", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Import Share Links from clipboard (Ctrl+V) 的本地化字符串。
///
@@ -951,6 +978,78 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Generate Policy Group from Multiple Profiles 的本地化字符串。
+ ///
+ public static string menuGenGroupMultipleServer {
+ get {
+ return ResourceManager.GetString("menuGenGroupMultipleServer", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Multi-Configuration Fallback by sing-box 的本地化字符串。
+ ///
+ public static string menuGenGroupMultipleServerSingBoxFallback {
+ get {
+ return ResourceManager.GetString("menuGenGroupMultipleServerSingBoxFallback", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Multi-Configuration LeastPing by sing-box 的本地化字符串。
+ ///
+ public static string menuGenGroupMultipleServerSingBoxLeastPing {
+ get {
+ return ResourceManager.GetString("menuGenGroupMultipleServerSingBoxLeastPing", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Multi-Configuration Fallback by Xray 的本地化字符串。
+ ///
+ public static string menuGenGroupMultipleServerXrayFallback {
+ get {
+ return ResourceManager.GetString("menuGenGroupMultipleServerXrayFallback", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Multi-Configuration LeastLoad by Xray 的本地化字符串。
+ ///
+ public static string menuGenGroupMultipleServerXrayLeastLoad {
+ get {
+ return ResourceManager.GetString("menuGenGroupMultipleServerXrayLeastLoad", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Multi-Configuration LeastPing by Xray 的本地化字符串。
+ ///
+ public static string menuGenGroupMultipleServerXrayLeastPing {
+ get {
+ return ResourceManager.GetString("menuGenGroupMultipleServerXrayLeastPing", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Multi-Configuration Random by Xray 的本地化字符串。
+ ///
+ public static string menuGenGroupMultipleServerXrayRandom {
+ get {
+ return ResourceManager.GetString("menuGenGroupMultipleServerXrayRandom", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Multi-Configuration RoundRobin by Xray 的本地化字符串。
+ ///
+ public static string menuGenGroupMultipleServerXrayRoundRobin {
+ get {
+ return ResourceManager.GetString("menuGenGroupMultipleServerXrayRoundRobin", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Global Hotkey Setting 的本地化字符串。
///
@@ -1320,6 +1419,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Remove Child Configuration 的本地化字符串。
+ ///
+ public static string menuRemoveChildServer {
+ get {
+ return ResourceManager.GetString("menuRemoveChildServer", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Remove duplicate Configurations 的本地化字符串。
///
@@ -1473,6 +1581,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Server List 的本地化字符串。
+ ///
+ public static string menuServerList {
+ get {
+ return ResourceManager.GetString("menuServerList", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Configurations 的本地化字符串。
///
@@ -1482,60 +1599,6 @@ namespace ServiceLib.Resx {
}
}
- ///
- /// 查找类似 Multi-Configuration to custom configuration 的本地化字符串。
- ///
- public static string menuSetDefaultMultipleServer {
- get {
- return ResourceManager.GetString("menuSetDefaultMultipleServer", resourceCulture);
- }
- }
-
- ///
- /// 查找类似 Multi-Configuration LeastPing by sing-box 的本地化字符串。
- ///
- public static string menuSetDefaultMultipleServerSingBoxLeastPing {
- get {
- return ResourceManager.GetString("menuSetDefaultMultipleServerSingBoxLeastPing", resourceCulture);
- }
- }
-
- ///
- /// 查找类似 Multi-Configuration LeastLoad by Xray 的本地化字符串。
- ///
- public static string menuSetDefaultMultipleServerXrayLeastLoad {
- get {
- return ResourceManager.GetString("menuSetDefaultMultipleServerXrayLeastLoad", resourceCulture);
- }
- }
-
- ///
- /// 查找类似 Multi-Configuration LeastPing by Xray 的本地化字符串。
- ///
- public static string menuSetDefaultMultipleServerXrayLeastPing {
- get {
- return ResourceManager.GetString("menuSetDefaultMultipleServerXrayLeastPing", resourceCulture);
- }
- }
-
- ///
- /// 查找类似 Multi-Configuration Random by Xray 的本地化字符串。
- ///
- public static string menuSetDefaultMultipleServerXrayRandom {
- get {
- return ResourceManager.GetString("menuSetDefaultMultipleServerXrayRandom", resourceCulture);
- }
- }
-
- ///
- /// 查找类似 Multi-Configuration RoundRobin by Xray 的本地化字符串。
- ///
- public static string menuSetDefaultMultipleServerXrayRoundRobin {
- get {
- return ResourceManager.GetString("menuSetDefaultMultipleServerXrayRoundRobin", resourceCulture);
- }
- }
-
///
/// 查找类似 Set as active Configuration (Enter) 的本地化字符串。
///
@@ -1995,6 +2058,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Please Add At Least One Configuration 的本地化字符串。
+ ///
+ public static string PleaseAddAtLeastOneServer {
+ get {
+ return ResourceManager.GetString("PleaseAddAtLeastOneServer", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Please fill Remarks 的本地化字符串。
///
@@ -2373,6 +2445,24 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Policy Group 的本地化字符串。
+ ///
+ public static string TbConfigTypePolicyGroup {
+ get {
+ return ResourceManager.GetString("TbConfigTypePolicyGroup", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Proxy Chain 的本地化字符串。
+ ///
+ public static string TbConfigTypeProxyChain {
+ get {
+ return ResourceManager.GetString("TbConfigTypeProxyChain", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Confirm 的本地化字符串。
///
@@ -2526,6 +2616,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Fallback 的本地化字符串。
+ ///
+ public static string TbFallback {
+ get {
+ return ResourceManager.GetString("TbFallback", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Fingerprint 的本地化字符串。
///
@@ -2643,6 +2742,24 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Most Stable 的本地化字符串。
+ ///
+ public static string TbLeastLoad {
+ get {
+ return ResourceManager.GetString("TbLeastLoad", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Lowest Latency 的本地化字符串。
+ ///
+ public static string TbLeastPing {
+ get {
+ return ResourceManager.GetString("TbLeastPing", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Address (IPv4, IPv6) 的本地化字符串。
///
@@ -2697,6 +2814,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Policy Group Type 的本地化字符串。
+ ///
+ public static string TbPolicyGroupType {
+ get {
+ return ResourceManager.GetString("TbPolicyGroupType", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Port 的本地化字符串。
///
@@ -2769,6 +2895,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Random 的本地化字符串。
+ ///
+ public static string TbRandom {
+ get {
+ return ResourceManager.GetString("TbRandom", resourceCulture);
+ }
+ }
+
///
/// 查找类似 v2ray Full Config Template 的本地化字符串。
///
@@ -2832,6 +2967,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Round Robin 的本地化字符串。
+ ///
+ public static string TbRoundRobin {
+ get {
+ return ResourceManager.GetString("TbRoundRobin", resourceCulture);
+ }
+ }
+
///
/// 查找类似 socks: local port, socks2: second local port, socks3: LAN port 的本地化字符串。
///
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
index 14b1ff6f..280231d8 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
@@ -1377,22 +1377,22 @@
مخفی و پورت می شود، با کاما (،) جدا می شود
-
- چند سرور به پیکربندی سفارشی
+
+ Generate Policy Group from Multiple Profiles
-
+
چند سرور تصادفی توسط Xray
-
+
چند سرور RoundRobin توسط Xray
-
+
چند سرور LeastPing توسط Xray
-
+
چند سرور LeastLoad توسط Xray
-
+
LeastPing چند سرور توسط sing-box
@@ -1512,4 +1512,52 @@
Applies globally by default, with built-in FakeIP filtering (sing-box only).
+
+ Please Add At Least One Configuration
+
+
+ Policy Group
+
+
+ Proxy Chain
+
+
+ Lowest Latency
+
+
+ Random
+
+
+ Round Robin
+
+
+ Most Stable
+
+
+ Policy Group Type
+
+
+ Add Policy Group Configuration
+
+
+ Add Proxy Chain Configuration
+
+
+ Add Child Configuration
+
+
+ Remove Child Configuration
+
+
+ Server List
+
+
+ Fallback
+
+
+ Multi-Configuration Fallback by sing-box
+
+
+ Multi-Configuration Fallback by Xray
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
index fb8c6a11..09796e2d 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
@@ -1377,22 +1377,22 @@
A portot lefedi, vesszővel (,) elválasztva
-
- Több konfiguráció egyéni konfigurációra
+
+ Generate Policy Group from Multiple Profiles
-
+
Több konfiguráció véletlenszerűen Xray szerint
-
+
Több konfiguráció RoundRobin Xray szerint
-
+
Több konfiguráció legkisebb pinggel Xray szerint
-
+
Több konfiguráció legkisebb terheléssel Xray szerint
-
+
Több konfiguráció legkisebb pinggel sing-box szerint
@@ -1512,4 +1512,52 @@
Applies globally by default, with built-in FakeIP filtering (sing-box only).
+
+ Please Add At Least One Configuration
+
+
+ Policy Group
+
+
+ Proxy Chain
+
+
+ Lowest Latency
+
+
+ Random
+
+
+ Round Robin
+
+
+ Most Stable
+
+
+ Policy Group Type
+
+
+ Add Policy Group Configuration
+
+
+ Add Proxy Chain Configuration
+
+
+ Add Child Configuration
+
+
+ Remove Child Configuration
+
+
+ Server List
+
+
+ Fallback
+
+
+ Multi-Configuration Fallback by sing-box
+
+
+ Multi-Configuration Fallback by Xray
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx
index 5b145da3..bf9f40a5 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.resx
@@ -1377,22 +1377,22 @@
Will cover the port, separate with commas (,)
-
- Multi-Configuration to custom configuration
+
+ Generate Policy Group from Multiple Profiles
-
+
Multi-Configuration Random by Xray
-
+
Multi-Configuration RoundRobin by Xray
-
+
Multi-Configuration LeastPing by Xray
-
+
Multi-Configuration LeastLoad by Xray
-
+
Multi-Configuration LeastPing by sing-box
@@ -1512,4 +1512,52 @@
Applies globally by default, with built-in FakeIP filtering (sing-box only).
+
+ Please Add At Least One Configuration
+
+
+ Policy Group
+
+
+ Proxy Chain
+
+
+ Lowest Latency
+
+
+ Random
+
+
+ Round Robin
+
+
+ Most Stable
+
+
+ Policy Group Type
+
+
+ Add Policy Group Configuration
+
+
+ Add Proxy Chain Configuration
+
+
+ Add Child Configuration
+
+
+ Remove Child Configuration
+
+
+ Server List
+
+
+ Fallback
+
+
+ Multi-Configuration Fallback by sing-box
+
+
+ Multi-Configuration Fallback by Xray
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
index 27c2d0a3..cde36277 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
@@ -1377,22 +1377,22 @@
Заменит указанный порт, перечисляйте через запятую (,)
-
- От мультиконфигурации к пользовательской конфигурации
+
+ Generate Policy Group from Multiple Profiles
-
+
Случайный (Xray)
-
+
Круговой (Xray)
-
+
Минимальное RTT (минимальное время туда-обратно) (Xray)
-
+
Минимальная нагрузка (Xray)
-
+
Минимальное RTT (минимальное время туда-обратно) (sing-box)
@@ -1512,4 +1512,52 @@
Applies globally by default, with built-in FakeIP filtering (sing-box only).
+
+ Please Add At Least One Configuration
+
+
+ Policy Group
+
+
+ Proxy Chain
+
+
+ Lowest Latency
+
+
+ Random
+
+
+ Round Robin
+
+
+ Most Stable
+
+
+ Policy Group Type
+
+
+ Add Policy Group Configuration
+
+
+ Add Proxy Chain Configuration
+
+
+ Add Child Configuration
+
+
+ Remove Child Configuration
+
+
+ Server List
+
+
+ Fallback
+
+
+ Multi-Configuration Fallback by sing-box
+
+
+ Multi-Configuration Fallback by Xray
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
index a269c0a5..1506de01 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
@@ -1374,22 +1374,22 @@
会覆盖端口,多组时用逗号 (,) 隔开
-
- 多配置文件产生自定义配置 (多选)
+
+ 多配置文件生成策略组
-
+
多配置文件随机 Xray
-
+
多配置文件负载均衡 Xray
-
+
多配置文件最低延迟 Xray
-
+
多配置文件最稳定 Xray
-
+
多配置文件最低延迟 sing-box
@@ -1509,4 +1509,52 @@
默认全局生效,内置 FakeIP 过滤,仅在 sing-box 中生效
+
+ 请至少添加一个配置文件
+
+
+ 策略组
+
+
+ 链式代理
+
+
+ 最低延迟
+
+
+ 随机
+
+
+ 负载均衡
+
+
+ 最稳定
+
+
+ 策略组类型
+
+
+ 添加策略组配置文件
+
+
+ 添加链式代理配置文件
+
+
+ 添加子配置文件
+
+
+ 删除子配置文件
+
+
+ 服务器列表
+
+
+ 故障转移
+
+
+ 多配置文件故障转移 sing-box
+
+
+ 多配置文件故障转移 Xray
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
index 81a01cfc..3101cb2f 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
@@ -1374,22 +1374,22 @@
會覆蓋埠,多組時用逗號 (,) 隔開
-
- 多設定檔產生自訂配置 (多選)
+
+ Generate Policy Group from Multiple Profiles
-
+
多設定檔隨機 Xray
-
+
多設定檔負載平衡 Xray
-
+
多設定檔最低延遲 Xray
-
+
多設定檔最穩定 Xray
-
+
多設定檔最低延遲 sing-box
@@ -1509,4 +1509,52 @@
Applies globally by default, with built-in FakeIP filtering (sing-box only).
+
+ Please Add At Least One Configuration
+
+
+ Policy Group
+
+
+ Proxy Chain
+
+
+ Lowest Latency
+
+
+ Random
+
+
+ Round Robin
+
+
+ Most Stable
+
+
+ Policy Group Type
+
+
+ Add Policy Group Configuration
+
+
+ Add Proxy Chain Configuration
+
+
+ Add Child Configuration
+
+
+ Remove Child Configuration
+
+
+ Server List
+
+
+ Fallback
+
+
+ Multi-Configuration Fallback by sing-box
+
+
+ Multi-Configuration Fallback by Xray
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs
index 4e24d8e2..fd77480a 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs
@@ -16,7 +16,7 @@ public partial class CoreConfigSingboxService(Config config)
try
{
if (node == null
- || node.Port <= 0)
+ || !node.IsValid())
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
@@ -28,6 +28,17 @@ public partial class CoreConfigSingboxService(Config config)
}
ret.Msg = ResUI.InitialConfiguration;
+
+ if (node?.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
+ {
+ switch (node.ConfigType)
+ {
+ case EConfigType.PolicyGroup:
+ return await GenerateClientMultipleLoadConfig(node);
+ case EConfigType.ProxyChain:
+ return await GenerateClientChainConfig(node);
+ }
+ }
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
if (result.IsNullOrEmpty())
@@ -142,12 +153,9 @@ public partial class CoreConfigSingboxService(Config config)
continue;
}
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
- if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
+ if (item is null || item.IsComplex() || !item.IsValid())
{
- if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
- {
- continue;
- }
+ continue;
}
//find unused port
@@ -187,27 +195,6 @@ public partial class CoreConfigSingboxService(Config config)
singboxConfig.inbounds.Add(inbound);
//outbound
- if (item is null)
- {
- continue;
- }
- if (item.ConfigType == EConfigType.Shadowsocks
- && !Global.SsSecuritiesInSingbox.Contains(item.Security))
- {
- continue;
- }
- if (item.ConfigType == EConfigType.VLESS
- && !Global.Flows.Contains(item.Flow))
- {
- continue;
- }
- if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan
- && item.StreamSecurity == Global.StreamSecurityReality
- && item.PublicKey.IsNullOrEmpty())
- {
- continue;
- }
-
var server = await GenServer(item);
if (server is null)
{
@@ -266,7 +253,8 @@ public partial class CoreConfigSingboxService(Config config)
var ret = new RetResult();
try
{
- if (node is not { Port: > 0 })
+ if (node == null
+ || !node.IsValid())
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
@@ -344,7 +332,7 @@ public partial class CoreConfigSingboxService(Config config)
}
}
- public async Task GenerateClientMultipleLoadConfig(List selecteds)
+ public async Task GenerateClientMultipleLoadConfig(ProfileItem parentNode)
{
var ret = new RetResult();
try
@@ -371,56 +359,77 @@ public partial class CoreConfigSingboxService(Config config)
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
+ singboxConfig.outbounds.RemoveAt(0);
await GenLog(singboxConfig);
await GenInbounds(singboxConfig);
- await GenRouting(singboxConfig);
- await GenExperimental(singboxConfig);
- singboxConfig.outbounds.RemoveAt(0);
- var proxyProfiles = new List();
- foreach (var it in selecteds)
- {
- if (!Global.SingboxSupportConfigType.Contains(it.ConfigType))
- {
- continue;
- }
- if (it.Port <= 0)
- {
- continue;
- }
- var item = await AppManager.Instance.GetProfileItem(it.IndexId);
- if (item is null)
- {
- continue;
- }
- if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
- {
- if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
- {
- continue;
- }
- }
- if (item.ConfigType == EConfigType.Shadowsocks
- && !Global.SsSecuritiesInSingbox.Contains(item.Security))
- {
- continue;
- }
- if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow))
- {
- continue;
- }
-
- //outbound
- proxyProfiles.Add(item);
- }
- if (proxyProfiles.Count <= 0)
+ var groupRet = await GenGroupOutbound(parentNode, singboxConfig);
+ if (groupRet != 0)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
- await GenOutboundsList(proxyProfiles, singboxConfig);
+ await GenRouting(singboxConfig);
+ await GenExperimental(singboxConfig);
+ await GenDns(null, singboxConfig);
+ await ConvertGeo2Ruleset(singboxConfig);
+
+ ret.Success = true;
+
+ ret.Data = await ApplyFullConfigTemplate(singboxConfig);
+ return ret;
+ }
+ catch (Exception ex)
+ {
+ Logging.SaveLog(_tag, ex);
+ ret.Msg = ResUI.FailedGenDefaultConfiguration;
+ return ret;
+ }
+ }
+
+ public async Task GenerateClientChainConfig(ProfileItem parentNode)
+ {
+ var ret = new RetResult();
+ try
+ {
+ if (_config == null)
+ {
+ ret.Msg = ResUI.CheckServerSettings;
+ return ret;
+ }
+
+ ret.Msg = ResUI.InitialConfiguration;
+
+ var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
+ var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
+ if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
+ {
+ ret.Msg = ResUI.FailedGetDefaultConfiguration;
+ return ret;
+ }
+
+ var singboxConfig = JsonUtils.Deserialize(result);
+ if (singboxConfig == null)
+ {
+ ret.Msg = ResUI.FailedGenDefaultConfiguration;
+ return ret;
+ }
+ singboxConfig.outbounds.RemoveAt(0);
+
+ await GenLog(singboxConfig);
+ await GenInbounds(singboxConfig);
+
+ var groupRet = await GenGroupOutbound(parentNode, singboxConfig);
+ if (groupRet != 0)
+ {
+ ret.Msg = ResUI.FailedGenDefaultConfiguration;
+ return ret;
+ }
+
+ await GenRouting(singboxConfig);
+ await GenExperimental(singboxConfig);
await GenDns(null, singboxConfig);
await ConvertGeo2Ruleset(singboxConfig);
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
index 9bd2502d..10c82eed 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
@@ -414,16 +414,19 @@ public partial class CoreConfigSingboxService
return 0;
}
- var domain = string.Empty;
+ List domain = new();
if (Utils.IsDomain(node.Address)) // normal outbound
{
- domain = node.Address;
+ domain.Add(node.Address);
}
- else if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty() && Utils.IsDomain(node.SpiderX)) // Tun2SocksAddress
+ if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty()) // Tun2SocksAddress
{
- domain = node.SpiderX;
+ domain.AddRange(Utils.String2List(node.SpiderX)
+ .Where(Utils.IsDomain)
+ .Distinct()
+ .ToList());
}
- if (domain.IsNullOrEmpty())
+ if (domain.Count == 0)
{
return 0;
}
@@ -432,7 +435,7 @@ public partial class CoreConfigSingboxService
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
{
server = server,
- domain = [domain],
+ domain = domain,
});
return await Task.FromResult(0);
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs
index 3f03b93c..46c90b23 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs
@@ -179,13 +179,21 @@ public partial class CoreConfigSingboxService
if (node.ConfigType == EConfigType.WireGuard)
{
var endpoint = JsonUtils.Deserialize(txtOutbound);
- await GenEndpoint(node, endpoint);
+ var ret = await GenEndpoint(node, endpoint);
+ if (ret != 0)
+ {
+ return null;
+ }
return endpoint;
}
else
{
var outbound = JsonUtils.Deserialize(txtOutbound);
- await GenOutbound(node, outbound);
+ var ret = await GenOutbound(node, outbound);
+ if (ret != 0)
+ {
+ return null;
+ }
return outbound;
}
}
@@ -196,6 +204,52 @@ public partial class CoreConfigSingboxService
return await Task.FromResult(null);
}
+ private async Task GenGroupOutbound(ProfileItem node, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false)
+ {
+ try
+ {
+ if (node.ConfigType is not (EConfigType.PolicyGroup or EConfigType.ProxyChain))
+ {
+ return -1;
+ }
+ var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
+ if (hasCycle)
+ {
+ return -1;
+ }
+
+ var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
+ if (childProfiles.Count <= 0)
+ {
+ return -1;
+ }
+ switch (node.ConfigType)
+ {
+ case EConfigType.PolicyGroup:
+ if (ignoreOriginChain)
+ {
+ await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
+ }
+ else
+ {
+ await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
+ }
+
+ break;
+ case EConfigType.ProxyChain:
+ await GenChainOutboundsList(childProfiles, singboxConfig, baseTagName);
+ break;
+ default:
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logging.SaveLog(_tag, ex);
+ }
+ return await Task.FromResult(0);
+ }
+
private async Task GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
{
try
@@ -410,7 +464,7 @@ public partial class CoreConfigSingboxService
return 0;
}
- private async Task GenOutboundsList(List nodes, SingboxConfig singboxConfig)
+ private async Task GenOutboundsListWithChain(List nodes, SingboxConfig singboxConfig, EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
{
try
{
@@ -438,6 +492,29 @@ public partial class CoreConfigSingboxService
{
index++;
+ if (node.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
+ {
+ var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
+ if (childProfiles.Count <= 0)
+ {
+ continue;
+ }
+ var childBaseTagName = $"{baseTagName}-{index}";
+ var ret = node.ConfigType switch
+ {
+ EConfigType.PolicyGroup =>
+ await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName),
+ EConfigType.ProxyChain =>
+ await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
+ _ => throw new NotImplementedException()
+ };
+ if (ret == 0)
+ {
+ proxyTags.Add(childBaseTagName);
+ }
+ continue;
+ }
+
// Handle proxy chain
string? prevTag = null;
var currentServer = await GenServer(node);
@@ -450,7 +527,7 @@ public partial class CoreConfigSingboxService
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
// current proxy
- currentServer.tag = $"{Global.ProxyTag}-{index}";
+ currentServer.tag = $"{baseTagName}-{index}";
proxyTags.Add(currentServer.tag);
if (!node.Subid.IsNullOrEmpty())
@@ -467,7 +544,7 @@ public partial class CoreConfigSingboxService
{
var prevOutbound = JsonUtils.Deserialize(txtOutbound);
await GenOutbound(prevNode, prevOutbound);
- prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
+ prevTag = $"prev-{baseTagName}-{++prevIndex}";
prevOutbound.tag = prevTag;
prevOutbounds.Add(prevOutbound);
}
@@ -508,16 +585,21 @@ public partial class CoreConfigSingboxService
var outUrltest = new Outbound4Sbox
{
type = "urltest",
- tag = $"{Global.ProxyTag}-auto",
+ tag = $"{baseTagName}-auto",
outbounds = proxyTags,
interrupt_exist_connections = false,
};
+ if (multipleLoad == EMultipleLoad.Fallback)
+ {
+ outUrltest.tolerance = 5000;
+ }
+
// Add selector outbound (manual selection)
var outSelector = new Outbound4Sbox
{
type = "selector",
- tag = Global.ProxyTag,
+ tag = baseTagName,
outbounds = JsonUtils.DeepCopy(proxyTags),
interrupt_exist_connections = false,
};
@@ -529,12 +611,12 @@ public partial class CoreConfigSingboxService
}
// Merge results: first the selector/urltest/proxies, then other outbounds, and finally prev outbounds
- resultOutbounds.AddRange(prevOutbounds);
- resultOutbounds.AddRange(singboxConfig.outbounds);
- singboxConfig.outbounds = resultOutbounds;
- singboxConfig.endpoints ??= new List();
- resultEndpoints.AddRange(singboxConfig.endpoints);
- singboxConfig.endpoints = resultEndpoints;
+ var serverList = new List();
+ serverList = serverList.Concat(prevOutbounds)
+ .Concat(resultOutbounds)
+ .Concat(resultEndpoints)
+ .ToList();
+ await AddRangeOutbounds(serverList, singboxConfig, baseTagName == Global.ProxyTag);
}
catch (Exception ex)
{
@@ -574,4 +656,163 @@ public partial class CoreConfigSingboxService
}
return null;
}
+
+ private async Task GenOutboundsList(List nodes, SingboxConfig singboxConfig, EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
+ {
+ var resultOutbounds = new List();
+ var resultEndpoints = new List(); // For endpoints
+ var proxyTags = new List(); // For selector and urltest outbounds
+ for (var i = 0; i < nodes.Count; i++)
+ {
+ var node = nodes[i];
+ if (node == null)
+ continue;
+ if (node.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
+ {
+ var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
+ if (childProfiles.Count <= 0)
+ {
+ continue;
+ }
+ var childBaseTagName = $"{baseTagName}-{i + 1}";
+ var ret = node.ConfigType switch
+ {
+ EConfigType.PolicyGroup =>
+ await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName),
+ EConfigType.ProxyChain =>
+ await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
+ _ => throw new NotImplementedException()
+ };
+ if (ret == 0)
+ {
+ proxyTags.Add(childBaseTagName);
+ }
+ continue;
+ }
+ var server = await GenServer(node);
+ if (server is null)
+ {
+ break;
+ }
+ server.tag = baseTagName + (i + 1).ToString();
+ if (server is Endpoints4Sbox endpoint)
+ {
+ resultEndpoints.Add(endpoint);
+ }
+ else if (server is Outbound4Sbox outbound)
+ {
+ resultOutbounds.Add(outbound);
+ }
+ proxyTags.Add(server.tag);
+ }
+ // Add urltest outbound (auto selection based on latency)
+ if (proxyTags.Count > 0)
+ {
+ var outUrltest = new Outbound4Sbox
+ {
+ type = "urltest",
+ tag = $"{baseTagName}-auto",
+ outbounds = proxyTags,
+ interrupt_exist_connections = false,
+ };
+ if (multipleLoad == EMultipleLoad.Fallback)
+ {
+ outUrltest.tolerance = 5000;
+ }
+ // Add selector outbound (manual selection)
+ var outSelector = new Outbound4Sbox
+ {
+ type = "selector",
+ tag = baseTagName,
+ outbounds = JsonUtils.DeepCopy(proxyTags),
+ interrupt_exist_connections = false,
+ };
+ outSelector.outbounds.Insert(0, outUrltest.tag);
+ // Insert these at the beginning
+ resultOutbounds.Insert(0, outUrltest);
+ resultOutbounds.Insert(0, outSelector);
+ }
+ var serverList = new List();
+ serverList = serverList.Concat(resultOutbounds)
+ .Concat(resultEndpoints)
+ .ToList();
+ await AddRangeOutbounds(serverList, singboxConfig, baseTagName == Global.ProxyTag);
+ return await Task.FromResult(0);
+ }
+
+ private async Task GenChainOutboundsList(List nodes, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag)
+ {
+ // Based on actual network flow instead of data packets
+ var nodesReverse = nodes.AsEnumerable().Reverse().ToList();
+ var resultOutbounds = new List();
+ var resultEndpoints = new List(); // For endpoints
+ for (var i = 0; i < nodesReverse.Count; i++)
+ {
+ var node = nodesReverse[i];
+ var server = await GenServer(node);
+
+ if (server is null)
+ {
+ break;
+ }
+
+ if (i == 0)
+ {
+ server.tag = baseTagName;
+ }
+ else
+ {
+ server.tag = baseTagName + i.ToString();
+ }
+
+ if (i != nodesReverse.Count - 1)
+ {
+ server.detour = baseTagName + (i + 1).ToString();
+ }
+
+ if (server is Endpoints4Sbox endpoint)
+ {
+ resultEndpoints.Add(endpoint);
+ }
+ else if (server is Outbound4Sbox outbound)
+ {
+ resultOutbounds.Add(outbound);
+ }
+ }
+ var serverList = new List();
+ serverList = serverList.Concat(resultOutbounds)
+ .Concat(resultEndpoints)
+ .ToList();
+ await AddRangeOutbounds(serverList, singboxConfig, baseTagName == Global.ProxyTag);
+ return await Task.FromResult(0);
+ }
+
+ private async Task AddRangeOutbounds(List servers, SingboxConfig singboxConfig, bool prepend = true)
+ {
+ try
+ {
+ if (servers is null || servers.Count <= 0)
+ {
+ return 0;
+ }
+ var outbounds = servers.Where(s => s is Outbound4Sbox).Cast().ToList();
+ var endpoints = servers.Where(s => s is Endpoints4Sbox).Cast().ToList();
+ singboxConfig.endpoints ??= new();
+ if (prepend)
+ {
+ singboxConfig.outbounds.InsertRange(0, outbounds);
+ singboxConfig.endpoints.InsertRange(0, endpoints);
+ }
+ else
+ {
+ singboxConfig.outbounds.AddRange(outbounds);
+ singboxConfig.endpoints.AddRange(endpoints);
+ }
+ }
+ catch (Exception ex)
+ {
+ Logging.SaveLog(_tag, ex);
+ }
+ return await Task.FromResult(0);
+ }
}
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs
index 21dfb101..3994e21b 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs
@@ -368,26 +368,38 @@ public partial class CoreConfigSingboxService
}
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
+
if (node == null
- || !Global.SingboxSupportConfigType.Contains(node.ConfigType))
+ || (!Global.SingboxSupportConfigType.Contains(node.ConfigType)
+ && node.ConfigType is not (EConfigType.PolicyGroup or EConfigType.ProxyChain)))
{
return Global.ProxyTag;
}
- var tag = Global.ProxyTag + node.IndexId.ToString();
+ var tag = $"{node.IndexId}-{Global.ProxyTag}";
if (singboxConfig.outbounds.Any(o => o.tag == tag)
|| (singboxConfig.endpoints != null && singboxConfig.endpoints.Any(e => e.tag == tag)))
{
return tag;
}
+ if (node.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
+ {
+ var ret = await GenGroupOutbound(node, singboxConfig, tag);
+ if (ret == 0)
+ {
+ return tag;
+ }
+ return Global.ProxyTag;
+ }
+
var server = await GenServer(node);
if (server is null)
{
return Global.ProxyTag;
}
- server.tag = Global.ProxyTag + node.IndexId.ToString();
+ server.tag = tag;
if (server is Endpoints4Sbox endpoint)
{
singboxConfig.endpoints ??= new();
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
index 24b41109..37c55bca 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
@@ -16,7 +16,7 @@ public partial class CoreConfigV2rayService(Config config)
try
{
if (node == null
- || node.Port <= 0)
+ || !node.IsValid())
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
@@ -30,6 +30,17 @@ public partial class CoreConfigV2rayService(Config config)
ret.Msg = ResUI.InitialConfiguration;
+ if (node?.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
+ {
+ switch (node.ConfigType)
+ {
+ case EConfigType.PolicyGroup:
+ return await GenerateClientMultipleLoadConfig(node);
+ case EConfigType.ProxyChain:
+ return await GenerateClientChainConfig(node);
+ }
+ }
+
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
if (result.IsNullOrEmpty())
{
@@ -71,7 +82,7 @@ public partial class CoreConfigV2rayService(Config config)
}
}
- public async Task GenerateClientMultipleLoadConfig(List selecteds, EMultipleLoad multipleLoad)
+ public async Task GenerateClientMultipleLoadConfig(ProfileItem parentNode)
{
var ret = new RetResult();
@@ -99,70 +110,50 @@ public partial class CoreConfigV2rayService(Config config)
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
+ v2rayConfig.outbounds.RemoveAt(0);
await GenLog(v2rayConfig);
await GenInbounds(v2rayConfig);
- await GenRouting(v2rayConfig);
- await GenDns(null, v2rayConfig);
- await GenStatistic(v2rayConfig);
- v2rayConfig.outbounds.RemoveAt(0);
- var proxyProfiles = new List();
- foreach (var it in selecteds)
- {
- if (!Global.XraySupportConfigType.Contains(it.ConfigType))
- {
- continue;
- }
- if (it.Port <= 0)
- {
- continue;
- }
- var item = await AppManager.Instance.GetProfileItem(it.IndexId);
- if (item is null)
- {
- continue;
- }
- if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
- {
- if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
- {
- continue;
- }
- }
- if (item.ConfigType == EConfigType.Shadowsocks
- && !Global.SsSecuritiesInSingbox.Contains(item.Security))
- {
- continue;
- }
- if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow))
- {
- continue;
- }
-
- //outbound
- proxyProfiles.Add(item);
- }
- if (proxyProfiles.Count <= 0)
+ var groupRet = await GenGroupOutbound(parentNode, v2rayConfig);
+ if (groupRet != 0)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
- await GenOutboundsList(proxyProfiles, v2rayConfig);
- //add balancers
- await GenBalancer(v2rayConfig, multipleLoad);
+ await GenRouting(v2rayConfig);
+ await GenDns(null, v2rayConfig);
+ await GenStatistic(v2rayConfig);
- var balancer = v2rayConfig.routing.balancers.First();
+ var defaultBalancerTag = $"{Global.ProxyTag}{Global.BalancerTagSuffix}";
//add rule
- var rules = v2rayConfig.routing.rules.Where(t => t.outboundTag == Global.ProxyTag).ToList();
- if (rules?.Count > 0)
+ var rules = v2rayConfig.routing.rules;
+ if (rules?.Count > 0 && ((v2rayConfig.routing.balancers?.Count ?? 0) > 0))
{
+ var balancerTagSet = v2rayConfig.routing.balancers
+ .Select(b => b.tag)
+ .ToHashSet();
+
foreach (var rule in rules)
{
- rule.outboundTag = null;
- rule.balancerTag = balancer.tag;
+ if (rule.outboundTag == null)
+ continue;
+
+ if (balancerTagSet.Contains(rule.outboundTag))
+ {
+ rule.balancerTag = rule.outboundTag;
+ rule.outboundTag = null;
+ continue;
+ }
+
+ var outboundWithSuffix = rule.outboundTag + Global.BalancerTagSuffix;
+ if (balancerTagSet.Contains(outboundWithSuffix))
+ {
+ rule.balancerTag = outboundWithSuffix;
+ rule.outboundTag = null;
+ }
}
}
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
@@ -170,7 +161,7 @@ public partial class CoreConfigV2rayService(Config config)
v2rayConfig.routing.rules.Add(new()
{
ip = ["0.0.0.0/0", "::/0"],
- balancerTag = balancer.tag,
+ balancerTag = defaultBalancerTag,
type = "field"
});
}
@@ -179,7 +170,7 @@ public partial class CoreConfigV2rayService(Config config)
v2rayConfig.routing.rules.Add(new()
{
network = "tcp,udp",
- balancerTag = balancer.tag,
+ balancerTag = defaultBalancerTag,
type = "field"
});
}
@@ -197,6 +188,63 @@ public partial class CoreConfigV2rayService(Config config)
}
}
+ public async Task GenerateClientChainConfig(ProfileItem parentNode)
+ {
+ var ret = new RetResult();
+
+ try
+ {
+ if (_config == null)
+ {
+ ret.Msg = ResUI.CheckServerSettings;
+ return ret;
+ }
+
+ ret.Msg = ResUI.InitialConfiguration;
+
+ string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
+ string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
+ if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
+ {
+ ret.Msg = ResUI.FailedGetDefaultConfiguration;
+ return ret;
+ }
+
+ var v2rayConfig = JsonUtils.Deserialize(result);
+ if (v2rayConfig == null)
+ {
+ ret.Msg = ResUI.FailedGenDefaultConfiguration;
+ return ret;
+ }
+ v2rayConfig.outbounds.RemoveAt(0);
+
+ await GenLog(v2rayConfig);
+ await GenInbounds(v2rayConfig);
+
+ var groupRet = await GenGroupOutbound(parentNode, v2rayConfig);
+ if (groupRet != 0)
+ {
+ ret.Msg = ResUI.FailedGenDefaultConfiguration;
+ return ret;
+ }
+
+ await GenRouting(v2rayConfig);
+ await GenDns(null, v2rayConfig);
+ await GenStatistic(v2rayConfig);
+
+ ret.Success = true;
+
+ ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
+ return ret;
+ }
+ catch (Exception ex)
+ {
+ Logging.SaveLog(_tag, ex);
+ ret.Msg = ResUI.FailedGenDefaultConfiguration;
+ return ret;
+ }
+ }
+
public async Task GenerateClientSpeedtestConfig(List selecteds)
{
var ret = new RetResult();
@@ -255,12 +303,9 @@ public partial class CoreConfigV2rayService(Config config)
continue;
}
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
- if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
+ if (item is null || item.IsComplex() || !item.IsValid())
{
- if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
- {
- continue;
- }
+ continue;
}
//find unused port
@@ -289,28 +334,6 @@ public partial class CoreConfigV2rayService(Config config)
it.Port = port;
it.AllowTest = true;
- //outbound
- if (item is null)
- {
- continue;
- }
- if (item.ConfigType == EConfigType.Shadowsocks
- && !Global.SsSecuritiesInXray.Contains(item.Security))
- {
- continue;
- }
- if (item.ConfigType == EConfigType.VLESS
- && !Global.Flows.Contains(item.Flow))
- {
- continue;
- }
- if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan
- && item.StreamSecurity == Global.StreamSecurityReality
- && item.PublicKey.IsNullOrEmpty())
- {
- continue;
- }
-
//inbound
Inbounds4Ray inbound = new()
{
@@ -321,6 +344,7 @@ public partial class CoreConfigV2rayService(Config config)
inbound.tag = inbound.protocol + inbound.port.ToString();
v2rayConfig.inbounds.Add(inbound);
+ //outbound
var outbound = JsonUtils.Deserialize(txtOutbound);
await GenOutbound(item, outbound);
outbound.tag = Global.ProxyTag + inbound.port.ToString();
@@ -354,7 +378,8 @@ public partial class CoreConfigV2rayService(Config config)
var ret = new RetResult();
try
{
- if (node is not { Port: > 0 })
+ if (node == null
+ || !node.IsValid())
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs
index 8d2476e6..660cc700 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs
@@ -2,34 +2,87 @@ namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigV2rayService
{
- private async Task GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad)
+ private async Task GenObservatory(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
{
- if (multipleLoad == EMultipleLoad.LeastPing)
+ // Collect all existing subject selectors from both observatories
+ var subjectSelectors = new List