diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props index 785e23d1..7bae47f6 100644 --- a/v2rayN/Directory.Build.props +++ b/v2rayN/Directory.Build.props @@ -1,7 +1,7 @@ - 7.14.9 + 7.14.10 diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index 9193271c..6f8fadf2 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -5,10 +5,10 @@ false - - - - + + + + diff --git a/v2rayN/ServiceLib/Common/JsonUtils.cs b/v2rayN/ServiceLib/Common/JsonUtils.cs index 6954e124..4fbaa0e1 100644 --- a/v2rayN/ServiceLib/Common/JsonUtils.cs +++ b/v2rayN/ServiceLib/Common/JsonUtils.cs @@ -9,6 +9,31 @@ public class JsonUtils { private static readonly string _tag = "JsonUtils"; + private static readonly JsonSerializerOptions _defaultDeserializeOptions = new() + { + PropertyNameCaseInsensitive = true, + ReadCommentHandling = JsonCommentHandling.Skip + }; + + private static readonly JsonSerializerOptions _defaultSerializeOptions = new() + { + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + private static readonly JsonSerializerOptions _nullValueSerializeOptions = new() + { + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.Never, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + private static readonly JsonDocumentOptions _defaultDocumentOptions = new() + { + CommentHandling = JsonCommentHandling.Skip + }; + /// /// DeepCopy /// @@ -34,11 +59,7 @@ public class JsonUtils { return default; } - var options = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }; - return JsonSerializer.Deserialize(strJson, options); + return JsonSerializer.Deserialize(strJson, _defaultDeserializeOptions); } catch { @@ -59,7 +80,7 @@ public class JsonUtils { return null; } - return JsonNode.Parse(strJson); + return JsonNode.Parse(strJson, nodeOptions: null, _defaultDocumentOptions); } catch { @@ -84,12 +105,7 @@ public class JsonUtils { return result; } - var options = new JsonSerializerOptions - { - WriteIndented = indented, - DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; + var options = nullValue ? _nullValueSerializeOptions : _defaultSerializeOptions; result = JsonSerializer.Serialize(obj, options); } catch (Exception ex) diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs index 49a359c0..358d4e9a 100644 --- a/v2rayN/ServiceLib/Common/Utils.cs +++ b/v2rayN/ServiceLib/Common/Utils.cs @@ -331,6 +331,32 @@ public class Utils .ToList(); } + public static Dictionary> ParseHostsToDictionary(string hostsContent) + { + var userHostsMap = hostsContent + .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) + .Select(line => line.Trim()) + // skip full-line comments + .Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith("#")) + // strip inline comments (truncate at '#') + .Select(line => + { + var index = line.IndexOf('#'); + return index >= 0 ? line.Substring(0, index).Trim() : line; + }) + // ensure line still contains valid parts + .Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' ')) + .Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries)) + .Where(parts => parts.Length >= 2) + .GroupBy(parts => parts[0]) + .ToDictionary( + group => group.Key, + group => group.SelectMany(parts => parts.Skip(1)).ToList() + ); + + return userHostsMap; + } + #endregion 转换函数 #region 数据检查 @@ -857,6 +883,55 @@ public class Utils return false; } + public static bool IsPackagedInstall() + { + try + { + if (IsWindows() || IsOSX()) + { + return false; + } + + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPIMAGE"))) + { + return true; + } + + var exePath = GetExePath(); + var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? ""; + var p = baseDir.Replace('\\', '/'); + + if (string.IsNullOrEmpty(p)) + { + return false; + } + + if (p.Contains("/.mount_", StringComparison.Ordinal)) + { + return true; + } + + if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (p.StartsWith("/usr/lib/v2rayN", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (p.StartsWith("/usr/share/v2rayN", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + catch + { + } + return false; + } + private static async Task GetLinuxUserId() { var arg = new List() { "-c", "id -u" }; diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 529d2023..a02b81bb 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -40,6 +40,7 @@ public class Global public const string ProxySetLinuxShellFileName = NamespaceSample + "proxy_set_linux_sh"; public const string KillAsSudoOSXShellFileName = NamespaceSample + "kill_as_sudo_osx_sh"; public const string KillAsSudoLinuxShellFileName = NamespaceSample + "kill_as_sudo_linux_sh"; + public const string SingboxFakeIPFilterFileName = NamespaceSample + "singbox_fakeip_filter"; public const string DefaultSecurity = "auto"; public const string DefaultNetwork = "tcp"; diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 602134a7..b402afb3 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -113,6 +113,10 @@ public static class ConfigHandler config.ConstItem ??= new ConstItem(); config.SimpleDNSItem ??= InitBuiltinSimpleDNS(); + if (config.SimpleDNSItem.GlobalFakeIp is null) + { + config.SimpleDNSItem.GlobalFakeIp = true; + } config.SpeedTestItem ??= new(); if (config.SpeedTestItem.SpeedTestTimeout < 10) @@ -2225,6 +2229,7 @@ public static class ConfigHandler UseSystemHosts = false, AddCommonHosts = true, FakeIP = false, + GlobalFakeIp = true, BlockBindingQuery = true, DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(), RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(), diff --git a/v2rayN/ServiceLib/Handler/Fmt/ClashFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/ClashFmt.cs index f711c51f..6c6f2bbf 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/ClashFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/ClashFmt.cs @@ -4,7 +4,7 @@ public class ClashFmt : BaseFmt { public static ProfileItem? ResolveFull(string strData, string? subRemarks) { - if (Contains(strData, "port", "socks-port", "proxies")) + if (Contains(strData, "external-controller", "-port", "proxies")) { var fileName = WriteAllText(strData, "yaml"); diff --git a/v2rayN/ServiceLib/Models/CheckUpdateModel.cs b/v2rayN/ServiceLib/Models/CheckUpdateModel.cs index 85225bc1..f06eedc9 100644 --- a/v2rayN/ServiceLib/Models/CheckUpdateModel.cs +++ b/v2rayN/ServiceLib/Models/CheckUpdateModel.cs @@ -1,10 +1,13 @@ +using ReactiveUI; +using ReactiveUI.Fody.Helpers; + namespace ServiceLib.Models; -public class CheckUpdateModel +public class CheckUpdateModel : ReactiveObject { public bool? IsSelected { get; set; } public string? CoreType { get; set; } - public string? Remarks { get; set; } + [Reactive] public string? Remarks { get; set; } public string? FileName { get; set; } public bool? IsFinished { get; set; } } diff --git a/v2rayN/ServiceLib/Models/ClashProxyModel.cs b/v2rayN/ServiceLib/Models/ClashProxyModel.cs index fd534309..014b0f12 100644 --- a/v2rayN/ServiceLib/Models/ClashProxyModel.cs +++ b/v2rayN/ServiceLib/Models/ClashProxyModel.cs @@ -1,7 +1,10 @@ +using ReactiveUI; +using ReactiveUI.Fody.Helpers; + namespace ServiceLib.Models; [Serializable] -public class ClashProxyModel +public class ClashProxyModel : ReactiveObject { public string? Name { get; set; } @@ -9,9 +12,9 @@ public class ClashProxyModel public string? Now { get; set; } - public int Delay { get; set; } + [Reactive] public int Delay { get; set; } - public string? DelayName { get; set; } + [Reactive] public string? DelayName { get; set; } public bool IsActive { get; set; } } diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index 72873da4..ef369990 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -260,6 +260,7 @@ public class SimpleDNSItem public bool? UseSystemHosts { get; set; } public bool? AddCommonHosts { get; set; } public bool? FakeIP { get; set; } + public bool? GlobalFakeIp { get; set; } public bool? BlockBindingQuery { get; set; } public string? DirectDNS { get; set; } public string? RemoteDNS { get; set; } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 1a6bb674..afa9ef49 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -2292,15 +2292,6 @@ namespace ServiceLib.Resx { } } - /// - /// 查找类似 Apply to Proxy Domains Only 的本地化字符串。 - /// - public static string TbApplyProxyDomainsOnly { - get { - return ResourceManager.GetString("TbApplyProxyDomainsOnly", resourceCulture); - } - } - /// /// 查找类似 Auto refresh 的本地化字符串。 /// @@ -2517,6 +2508,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Applies globally by default, with built-in FakeIP filtering (sing-box only). 的本地化字符串。 + /// + public static string TbFakeIPTips { + get { + return ResourceManager.GetString("TbFakeIPTips", resourceCulture); + } + } + /// /// 查找类似 Fingerprint 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index f4b279da..8537bbd6 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1452,9 +1452,6 @@ DNS Hosts: ("domain1 ip1 ip2" per line) - - Apply to Proxy Domains Only - Basic DNS Settings @@ -1512,4 +1509,7 @@ Select Profile + + Applies globally by default, with built-in FakeIP filtering (sing-box only). + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 800fa00f..e801c796 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1455,9 +1455,6 @@ DNS Hosts: ("domain1 ip1 ip2" per line) - - Apply to Proxy Domains Only - Basic DNS Settings @@ -1515,4 +1512,7 @@ Select Profile + + Applies globally by default, with built-in FakeIP filtering (sing-box only). + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index a13bd00a..74bee097 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1452,9 +1452,6 @@ DNS Hosts: ("domain1 ip1 ip2" per line) - - Apply to Proxy Domains Only - Basic DNS Settings @@ -1512,4 +1509,7 @@ Select Profile + + Applies globally by default, with built-in FakeIP filtering (sing-box only). + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index f9bfbd68..1b218cf6 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1452,9 +1452,6 @@ DNS hosts: (каждая строка в формате "domain1 ip1 ip2") - - Применять только к доменам через прокси - Базовые настройки DNS @@ -1512,4 +1509,7 @@ Select Profile + + Applies globally by default, with built-in FakeIP filtering (sing-box only). + \ 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 5c55463c..e85ed53c 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1449,9 +1449,6 @@ DNS Hosts:(“域名1 ip1 ip2” 一行一个) - - 仅对代理域名生效 - DNS 基础设置 @@ -1509,4 +1506,7 @@ 选择配置文件 + + 默认全局生效,内置 FakeIP 过滤,仅在 sing-box 中生效 + \ 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 06b651a9..4b458924 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1449,9 +1449,6 @@ DNS Hosts: ("domain1 ip1 ip2" per line) - - Apply to Proxy Domains Only - Basic DNS Settings @@ -1509,4 +1506,7 @@ Select Profile + + Applies globally by default, with built-in FakeIP filtering (sing-box only). + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Sample/singbox_fakeip_filter b/v2rayN/ServiceLib/Sample/singbox_fakeip_filter new file mode 100644 index 00000000..860ab0eb --- /dev/null +++ b/v2rayN/ServiceLib/Sample/singbox_fakeip_filter @@ -0,0 +1,92 @@ +{ + "domain": [ + "amobile.music.tc.qq.com", + "api-jooxtt.sanook.com", + "api.joox.com", + "aqqmusic.tc.qq.com", + "dl.stream.qqmusic.qq.com", + "ff.dorado.sdo.com", + "heartbeat.belkin.com", + "isure.stream.qqmusic.qq.com", + "joox.com", + "lens.l.google.com", + "localhost.ptlogin2.qq.com", + "localhost.sec.qq.com", + "mesu.apple.com", + "mobileoc.music.tc.qq.com", + "music.taihe.com", + "musicapi.taihe.com", + "na.b.g-tun.com", + "proxy.golang.org", + "ps.res.netease.com", + "shark007.net", + "songsearch.kugou.com", + "static.adtidy.org", + "streamoc.music.tc.qq.com", + "swcdn.apple.com", + "swdist.apple.com", + "swdownload.apple.com", + "swquery.apple.com", + "swscan.apple.com", + "trackercdn.kugou.com", + "xnotify.xboxlive.com" + ], + "domain_keyword": [ + "ntp", + "stun", + "time" + ], + "domain_regex": [ + "^[^.]+$", + "^[^.]+\\.[^.]+\\.xboxlive\\.com$", + "^localhost\\.[^.]+\\.weixin\\.qq\\.com$", + "^mijia\\scloud$", + "^xbox\\.[^.]+\\.microsoft\\.com$", + "^xbox\\.[^.]+\\.[^.]+\\.microsoft\\.com$" + ], + "domain_suffix": [ + "126.net", + "3gppnetwork.org", + "battle.net", + "battlenet.com.cn", + "cdn.nintendo.net", + "cmbchina.com", + "cmbimg.com", + "ff14.sdo.com", + "ffxiv.com", + "finalfantasyxiv.com", + "gcloudcs.com", + "home.arpa", + "invalid", + "kuwo.cn", + "lan", + "linksys.com", + "linksyssmartwifi.com", + "local", + "localdomain", + "localhost", + "market.xiaomi.com", + "mcdn.bilivideo.cn", + "media.dssott.com", + "msftconnecttest.com", + "msftncsi.com", + "music.163.com", + "music.migu.cn", + "n0808.com", + "nflxvideo.net", + "oray.com", + "orayimg.com", + "router.asus.com", + "sandai.net", + "square-enix.com", + "srv.nintendo.net", + "steamcontent.com", + "uu.163.com", + "wargaming.net", + "wggames.cn", + "wotgame.cn", + "wowsgame.cn", + "xiami.com", + "y.qq.com" + ] +} \ No newline at end of file diff --git a/v2rayN/ServiceLib/ServiceLib.csproj b/v2rayN/ServiceLib/ServiceLib.csproj index 9cd69952..c026edf8 100644 --- a/v2rayN/ServiceLib/ServiceLib.csproj +++ b/v2rayN/ServiceLib/ServiceLib.csproj @@ -44,6 +44,7 @@ + diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs index 2f0f8015..00bb14d9 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs @@ -33,6 +33,15 @@ public partial class CoreConfigSingboxService lastRule.Ip?.Contains("0.0.0.0/0") == true); } singboxConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag; + if ((!useDirectDns) && simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == false) + { + singboxConfig.dns.rules.Add(new() + { + server = Global.SingboxFakeDNSTag, + query_type = new List { 1, 28 }, // A and AAAA + rewrite_ttl = 1, + }); + } // Tun2SocksAddress if (node != null && Utils.IsDomain(node.Address)) @@ -94,17 +103,7 @@ public partial class CoreConfigSingboxService if (!simpleDNSItem.Hosts.IsNullOrEmpty()) { - var userHostsMap = simpleDNSItem.Hosts - .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) - .Select(line => line.Trim()) - .Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' ')) - .Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries)) - .Where(parts => parts.Length >= 2) - .GroupBy(parts => parts[0]) - .ToDictionary( - group => group.Key, - group => group.SelectMany(parts => parts.Skip(1)).ToList() - ); + var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts); foreach (var kvp in userHostsMap) { @@ -197,6 +196,28 @@ public partial class CoreConfigSingboxService }); } + if (simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == true) + { + var fakeipFilterRule = JsonUtils.Deserialize(EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName)); + fakeipFilterRule.invert = true; + var rule4Fake = new Rule4Sbox + { + server = Global.SingboxFakeDNSTag, + type = "logical", + mode = "and", + rewrite_ttl = 1, + rules = new List + { + new() { + query_type = new List { 1, 28 }, // A and AAAA + }, + fakeipFilterRule, + } + }; + + singboxConfig.dns.rules.Add(rule4Fake); + } + var routing = await ConfigHandler.GetDefaultRouting(_config); if (routing == null) return 0; @@ -276,10 +297,12 @@ public partial class CoreConfigSingboxService } else { - if (simpleDNSItem.FakeIP == true) + if (simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == false) { var rule4Fake = JsonUtils.DeepCopy(rule); rule4Fake.server = Global.SingboxFakeDNSTag; + rule4Fake.query_type = new List { 1, 28 }; // A and AAAA + rule4Fake.rewrite_ttl = 1; singboxConfig.dns.rules.Add(rule4Fake); } rule.server = Global.SingboxRemoteDNSTag; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs index 24804c50..21dfb101 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs @@ -71,6 +71,37 @@ public partial class CoreConfigSingboxService }); } + var hostsDomains = new List(); + var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); + if (dnsItem == null || dnsItem.Enabled == false) + { + var simpleDNSItem = _config.SimpleDNSItem; + if (!simpleDNSItem.Hosts.IsNullOrEmpty()) + { + var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts); + foreach (var kvp in userHostsMap) + { + hostsDomains.Add(kvp.Key); + } + } + if (simpleDNSItem.UseSystemHosts == true) + { + var systemHostsMap = Utils.GetSystemHosts(); + foreach (var kvp in systemHostsMap) + { + hostsDomains.Add(kvp.Key); + } + } + } + if (hostsDomains.Count > 0) + { + singboxConfig.route.rules.Add(new() + { + action = "resolve", + domain = hostsDomains, + }); + } + singboxConfig.route.rules.Add(new() { outbound = Global.DirectTag, @@ -343,6 +374,13 @@ public partial class CoreConfigSingboxService return Global.ProxyTag; } + var tag = Global.ProxyTag + node.IndexId.ToString(); + if (singboxConfig.outbounds.Any(o => o.tag == tag) + || (singboxConfig.endpoints != null && singboxConfig.endpoints.Any(e => e.tag == tag))) + { + return tag; + } + var server = await GenServer(node); if (server is null) { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs index 6f64e923..744286d4 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs @@ -261,17 +261,7 @@ public partial class CoreConfigV2rayService if (!simpleDNSItem.Hosts.IsNullOrEmpty()) { - var userHostsMap = simpleDNSItem.Hosts - .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) - .Select(line => line.Trim()) - .Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' ')) - .Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries)) - .Where(parts => parts.Length >= 2) - .GroupBy(parts => parts[0]) - .ToDictionary( - group => group.Key, - group => group.SelectMany(parts => parts.Skip(1)).ToList() - ); + var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts); foreach (var kvp in userHostsMap) { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs index 1cb46bf2..d39ea833 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs @@ -131,10 +131,16 @@ public partial class CoreConfigV2rayService return Global.ProxyTag; } + var tag = Global.ProxyTag + node.IndexId.ToString(); + if (v2rayConfig.outbounds.Any(p => p.tag == tag)) + { + return tag; + } + var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); var outbound = JsonUtils.Deserialize(txtOutbound); await GenOutbound(node, outbound); - outbound.tag = Global.ProxyTag + node.IndexId.ToString(); + outbound.tag = tag; v2rayConfig.outbounds.Add(outbound); return outbound.tag; diff --git a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs index 23839141..4c9c0550 100644 --- a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs @@ -63,6 +63,16 @@ public class CheckUpdateViewModel : MyReactiveObject private CheckUpdateModel GetCheckUpdateModel(string coreType) { + if (coreType == _v2rayN && Utils.IsPackagedInstall()) + { + return new() + { + IsSelected = false, + CoreType = coreType, + Remarks = ResUI.menuCheckUpdate + " (Not Support)", + }; + } + return new() { IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType) ?? true, @@ -104,6 +114,11 @@ public class CheckUpdateViewModel : MyReactiveObject } else if (item.CoreType == _v2rayN) { + if (Utils.IsPackagedInstall()) + { + await UpdateView(_v2rayN, "Not Support"); + continue; + } await CheckUpdateN(EnableCheckPreReleaseUpdate); } else if (item.CoreType == ECoreType.Xray.ToString()) @@ -334,9 +349,6 @@ public class CheckUpdateViewModel : MyReactiveObject { return; } - - var itemCopy = JsonUtils.DeepCopy(found); - itemCopy.Remarks = model.Remarks; - CheckUpdateModels.Replace(found, itemCopy); + found.Remarks = model.Remarks; } } diff --git a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs index 54cbdc8d..343d49df 100644 --- a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs @@ -391,7 +391,6 @@ public class ClashProxiesViewModel : MyReactiveObject public async Task ProxiesDelayTestResult(SpeedTestResult result) { - //UpdateHandler(false, $"{item.name}={result}"); var detail = ProxyDetails.FirstOrDefault(it => it.Name == result.IndexId); if (detail == null) { @@ -414,7 +413,6 @@ public class ClashProxiesViewModel : MyReactiveObject detail.Delay = _delayTimeout; detail.DelayName = string.Empty; } - ProxyDetails.Replace(detail, JsonUtils.DeepCopy(detail)); } #endregion proxy function diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs index 14216fee..4c0fd2b9 100644 --- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs @@ -293,7 +293,6 @@ public class ProfilesViewModel : MyReactiveObject { item.SpeedVal = result.Speed ?? string.Empty; } - //_profileItems.Replace(item, JsonUtils.DeepCopy(item)); } public async Task UpdateStatistics(ServerSpeedItem update) @@ -314,17 +313,6 @@ public class ProfilesViewModel : MyReactiveObject item.TodayUp = Utils.HumanFy(update.TodayUp); item.TotalDown = Utils.HumanFy(update.TotalDown); item.TotalUp = Utils.HumanFy(update.TotalUp); - - //if (SelectedProfile?.IndexId == item.IndexId) - //{ - // var temp = JsonUtils.DeepCopy(item); - // _profileItems.Replace(item, temp); - // SelectedProfile = temp; - //} - //else - //{ - // _profileItems.Replace(item, JsonUtils.DeepCopy(item)); - //} } } catch diff --git a/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayN/v2rayN.Desktop/App.axaml.cs index 5303eb6c..aa357ca4 100644 --- a/v2rayN/v2rayN.Desktop/App.axaml.cs +++ b/v2rayN/v2rayN.Desktop/App.axaml.cs @@ -1,7 +1,6 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using ServiceLib.Manager; using Splat; using v2rayN.Desktop.Common; using v2rayN.Desktop.Views; diff --git a/v2rayN/v2rayN.Desktop/Base/WindowBase.cs b/v2rayN/v2rayN.Desktop/Base/WindowBase.cs index c2c9a0bc..10105d2e 100644 --- a/v2rayN/v2rayN.Desktop/Base/WindowBase.cs +++ b/v2rayN/v2rayN.Desktop/Base/WindowBase.cs @@ -1,7 +1,6 @@ using Avalonia; using Avalonia.Interactivity; using Avalonia.ReactiveUI; -using ServiceLib.Manager; namespace v2rayN.Desktop.Base; diff --git a/v2rayN/v2rayN.Desktop/GlobalUsings.cs b/v2rayN/v2rayN.Desktop/GlobalUsings.cs index bc789ab0..4f2b931d 100644 --- a/v2rayN/v2rayN.Desktop/GlobalUsings.cs +++ b/v2rayN/v2rayN.Desktop/GlobalUsings.cs @@ -1,8 +1,9 @@ -global using ServiceLib; +global using ServiceLib; global using ServiceLib.Base; global using ServiceLib.Common; global using ServiceLib.Enums; global using ServiceLib.Handler; +global using ServiceLib.Manager; global using ServiceLib.Models; global using ServiceLib.Resx; -global using ServiceLib.ViewModels; \ No newline at end of file +global using ServiceLib.ViewModels; diff --git a/v2rayN/v2rayN.Desktop/Program.cs b/v2rayN/v2rayN.Desktop/Program.cs index 58ddb9f9..4b534324 100644 --- a/v2rayN/v2rayN.Desktop/Program.cs +++ b/v2rayN/v2rayN.Desktop/Program.cs @@ -1,6 +1,5 @@ using Avalonia; using Avalonia.ReactiveUI; -using ServiceLib.Manager; using v2rayN.Desktop.Common; namespace v2rayN.Desktop; diff --git a/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs b/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs index 21899bfc..3473cf23 100644 --- a/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs +++ b/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs @@ -8,7 +8,6 @@ using Avalonia.Styling; using ReactiveUI; using ReactiveUI.Fody.Helpers; using Semi.Avalonia; -using ServiceLib.Manager; namespace v2rayN.Desktop.ViewModels; diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index ab6bc223..7aa6a0be 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -400,7 +400,7 @@ Grid.Column="1" Width="400" Margin="{StaticResource Margin4}" - Watermark="1000:2000,3000:4000" /> + Watermark="1000-2000,3000,4000" />