From a6b12aa718778a05f6901bdff2ac4cf07a7ca118 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 25 Dec 2025 16:35:55 +0800 Subject: [PATCH 01/20] Refactor --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 62 +--- v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs | 9 +- v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs | 11 +- v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs | 9 +- .../Manager/ActionPrecheckManager.cs | 27 +- v2rayN/ServiceLib/Manager/AppManager.cs | 45 ++- .../ServiceLib/Manager/GroupProfileManager.cs | 175 +++++++++ .../Manager/ProfileGroupItemManager.cs | 343 ------------------ v2rayN/ServiceLib/Models/ProfileGroupItem.cs | 1 + v2rayN/ServiceLib/Models/ProfileItem.cs | 24 +- v2rayN/ServiceLib/Models/ProtocolExtraItem.cs | 27 ++ .../CoreConfig/Singbox/SingboxDnsService.cs | 2 +- .../Singbox/SingboxOutboundService.cs | 35 +- .../CoreConfig/V2ray/V2rayOutboundService.cs | 28 +- .../ViewModels/AddGroupServerViewModel.cs | 51 ++- .../ViewModels/AddServerViewModel.cs | 19 + .../Views/AddServerWindow.axaml.cs | 8 +- v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 8 +- 18 files changed, 388 insertions(+), 496 deletions(-) create mode 100644 v2rayN/ServiceLib/Manager/GroupProfileManager.cs create mode 100644 v2rayN/ServiceLib/Models/ProtocolExtraItem.cs diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index f9ff4494..b5fdea2b 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -258,6 +258,7 @@ public static class ConfigHandler item.CertSha = profileItem.CertSha; item.EchConfigList = profileItem.EchConfigList; item.EchForceQuery = profileItem.EchForceQuery; + item.JsonData = profileItem.JsonData; } var ret = item.ConfigType switch @@ -360,11 +361,6 @@ public static class ConfigHandler { } } - else if (profileItem.ConfigType.IsGroupType()) - { - var profileGroupItem = await AppManager.Instance.GetProfileGroupItem(it.IndexId); - await AddGroupServerCommon(config, profileItem, profileGroupItem, true); - } else { await AddServerCommon(config, profileItem, true); @@ -967,9 +963,11 @@ public static class ConfigHandler profileItem.Path = profileItem.Path.TrimEx(); profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx(); - if (!Global.Flows.Contains(profileItem.Flow)) + var extraItem = profileItem.GetExtraItem(); + + if (!Global.Flows.Contains(extraItem.Flow ?? string.Empty)) { - profileItem.Flow = Global.Flows.First(); + extraItem.Flow = Global.Flows.First(); } if (profileItem.Id.IsNullOrEmpty()) { @@ -980,6 +978,8 @@ public static class ConfigHandler profileItem.Security = Global.None; } + profileItem.SetExtraItem(extraItem); + await AddServerCommon(config, profileItem, toFile); return 0; @@ -1082,37 +1082,6 @@ 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.IndexId = 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 @@ -1128,6 +1097,8 @@ public static class ConfigHandler return false; } + var extraItem = o.GetExtraItem(); + return o.ConfigType == n.ConfigType && AreEqual(o.Address, n.Address) && o.Port == n.Port @@ -1138,7 +1109,7 @@ public static class ConfigHandler && AreEqual(o.RequestHost, n.RequestHost) && AreEqual(o.Path, n.Path) && (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity) - && AreEqual(o.Flow, n.Flow) + && AreEqual(extraItem.Flow, extraItem.Flow) && AreEqual(o.Sni, n.Sni) && AreEqual(o.Alpn, n.Alpn) && AreEqual(o.Fingerprint, n.Fingerprint) @@ -1199,7 +1170,7 @@ public static class ConfigHandler var indexId = Utils.GetGuid(false); var childProfileIndexId = Utils.List2String(selecteds.Select(p => p.IndexId).ToList()); - var remark = subId.IsNullOrEmpty() ? string.Empty : $"{(await AppManager.Instance.GetSubItem(subId)).Remarks} "; + var remark = subId.IsNullOrEmpty() ? string.Empty : $"{(await AppManager.Instance.GetSubItem(subId))?.Remarks} "; if (coreType == ECoreType.Xray) { remark += multipleLoad switch @@ -1233,13 +1204,12 @@ public static class ConfigHandler { profile.Subid = subId; } - var profileGroup = new ProfileGroupItem + var extraItem = new ProtocolExtraItem { - ChildItems = childProfileIndexId, - MultipleLoad = multipleLoad, - IndexId = indexId, + ChildItems = childProfileIndexId, MultipleLoad = multipleLoad, }; - var ret = await AddGroupServerCommon(config, profile, profileGroup, true); + profile.SetExtraItem(extraItem); + var ret = await AddServerCommon(config, profile, true); result.Success = ret == 0; result.Data = indexId; return result; @@ -1261,7 +1231,7 @@ public static class ConfigHandler var tun2SocksAddress = node.Address; if (node.ConfigType.IsGroupType()) { - var lstAddresses = (await ProfileGroupItemManager.GetAllChildDomainAddresses(node.IndexId)).ToList(); + var lstAddresses = (await GroupProfileManager.GetAllChildDomainAddresses(node)).ToList(); if (lstAddresses.Count > 0) { tun2SocksAddress = Utils.List2String(lstAddresses); diff --git a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs index 8bd3c3de..a2930362 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs @@ -21,9 +21,10 @@ public class BaseFmt protected static int ToUriQuery(ProfileItem item, string? securityDef, ref Dictionary dicQuery) { - if (item.Flow.IsNotEmpty()) + var extraItem = item.GetExtraItem(); + if (!extraItem.Flow.IsNullOrEmpty()) { - dicQuery.Add("flow", item.Flow); + dicQuery.Add("flow", extraItem.Flow); } if (item.StreamSecurity.IsNotEmpty()) @@ -208,7 +209,9 @@ public class BaseFmt protected static int ResolveUriQuery(NameValueCollection query, ref ProfileItem item) { - item.Flow = GetQueryValue(query, "flow"); + var extraItem = item.GetExtraItem(); + extraItem.Flow = GetQueryValue(query, "flow"); + item.SetExtraItem(extraItem); item.StreamSecurity = GetQueryValue(query, "security"); item.Sni = GetQueryValue(query, "sni"); item.Alpn = GetQueryDecoded(query, "alpn"); diff --git a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs index d92bd0c8..fe6f9168 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs @@ -24,11 +24,15 @@ public class Hysteria2Fmt : BaseFmt var query = Utils.ParseQueryString(url.Query); ResolveUriQuery(query, ref item); item.Path = GetQueryDecoded(query, "obfs-password"); - item.Ports = GetQueryDecoded(query, "mport"); if (item.CertSha.IsNullOrEmpty()) { item.CertSha = GetQueryDecoded(query, "pinSHA256"); } + ProtocolExtraItem extraItem = new() + { + Ports = GetQueryDecoded(query, "mport") + }; + item.SetExtraItem(extraItem); return item; } @@ -55,9 +59,10 @@ public class Hysteria2Fmt : BaseFmt dicQuery.Add("obfs", "salamander"); dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path)); } - if (item.Ports.IsNotEmpty()) + var extra = item.GetExtraItem(); + if (extra?.Ports?.IsNotEmpty() ?? false) { - dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-'))); + dicQuery.Add("mport", Utils.UrlEncode(extra.Ports.Replace(':', '-'))); } if (!item.CertSha.IsNullOrEmpty()) { diff --git a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs index e6535d6e..6bab8a50 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs @@ -23,6 +23,8 @@ public class VmessFmt : BaseFmt { return null; } + + var extraItem = item?.GetExtraItem(); var vmessQRCode = new VmessQRCode { v = item.ConfigVersion, @@ -30,7 +32,7 @@ public class VmessFmt : BaseFmt add = item.Address, port = item.Port, id = item.Id, - aid = item.AlterId, + aid = int.TryParse(extraItem?.AlterId, out var result) ? result : 0, scy = item.Security, net = item.Network, type = item.HeaderType, @@ -76,7 +78,10 @@ public class VmessFmt : BaseFmt item.Address = Utils.ToString(vmessQRCode.add); item.Port = vmessQRCode.port; item.Id = Utils.ToString(vmessQRCode.id); - item.AlterId = vmessQRCode.aid; + item.SetExtraItem(new ProtocolExtraItem + { + AlterId = vmessQRCode.aid.ToString(), + }); item.Security = Utils.ToString(vmessQRCode.scy); item.Security = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity; diff --git a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs index 965335cc..e101ba53 100644 --- a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs +++ b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs @@ -128,6 +128,8 @@ public class ActionPrecheckManager } } + var extraItem = item.GetExtraItem(); + switch (item.ConfigType) { case EConfigType.VMess: @@ -144,7 +146,7 @@ public class ActionPrecheckManager errors.Add(string.Format(ResUI.InvalidProperty, "Id")); } - if (!Global.Flows.Contains(item.Flow)) + if (!Global.Flows.Contains(extraItem.Flow ?? string.Empty)) { errors.Add(string.Format(ResUI.InvalidProperty, "Flow")); } @@ -202,37 +204,22 @@ public class ActionPrecheckManager { var errors = new List(); - ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group); - if (group is null || group.NotHasChild()) - { - errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks)); - return errors; - } - - var hasCycle = ProfileGroupItemManager.HasCycle(item.IndexId); + var hasCycle = await GroupProfileManager.HasCycle(item.IndexId, item.GetExtraItem()); if (hasCycle) { errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks)); return errors; } - var childIds = new List(); - var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group); - childIds.AddRangeSafe(subItems.Select(p => p.IndexId)); - childIds.AddRangeSafe(Utils.String2List(group.ChildItems)); + var (childItems, _) = await GroupProfileManager.GetChildProfileItems(item); - foreach (var child in childIds) + foreach (var childItem in childItems) { var childErrors = new List(); - if (child.IsNullOrEmpty()) - { - continue; - } - var childItem = await AppManager.Instance.GetProfileItem(child); if (childItem is null) { - childErrors.Add(string.Format(ResUI.NodeTagNotExist, child)); + childErrors.Add(string.Format(ResUI.NodeTagNotExist, "")); continue; } diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 6c20022a..4c978934 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -94,6 +94,8 @@ public sealed class AppManager _ = StatePort; _ = StatePort2; + _ = MigrateProfileExtra(); + return true; } @@ -225,15 +227,6 @@ public sealed class AppManager return await SQLiteHelper.Instance.TableAsync().FirstOrDefaultAsync(it => it.Remarks == remarks); } - public async Task GetProfileGroupItem(string indexId) - { - if (indexId.IsNullOrEmpty()) - { - return null; - } - return await SQLiteHelper.Instance.TableAsync().FirstOrDefaultAsync(it => it.IndexId == indexId); - } - public async Task?> RoutingItems() { return await SQLiteHelper.Instance.TableAsync().OrderBy(t => t.Sort).ToListAsync(); @@ -264,6 +257,40 @@ public sealed class AppManager return await SQLiteHelper.Instance.TableAsync().FirstOrDefaultAsync(it => it.CoreType == eCoreType); } + public async Task MigrateProfileExtra() + { + var list = await SQLiteHelper.Instance.TableAsync().ToListAsync(); + foreach (var item in list) + { + if (!item.JsonData.IsNullOrEmpty()) + { + return; + } + ProtocolExtraItem extra = new() + { + AlterId = item.AlterId.ToString(), + Flow = item.Flow.IsNotEmpty() ? item.Flow : null, + Ports = item.Ports.IsNotEmpty() ? item.Ports : null, + }; + if (item.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain) + { + extra.GroupType = nameof(item.ConfigType); + ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var groupItem); + if (groupItem != null && !groupItem.NotHasChild()) + { + extra.ChildItems = groupItem.ChildItems; + extra.SubChildItems = groupItem.SubChildItems; + extra.Filter = groupItem.Filter; + extra.MultipleLoad = groupItem.MultipleLoad; + } + } + item.SetExtraItem(extra); + await SQLiteHelper.Instance.UpdateAsync(item); + } + + await ProfileGroupItemManager.Instance.ClearAll(); + } + #endregion SqliteHelper #region Core Type diff --git a/v2rayN/ServiceLib/Manager/GroupProfileManager.cs b/v2rayN/ServiceLib/Manager/GroupProfileManager.cs new file mode 100644 index 00000000..af2029fa --- /dev/null +++ b/v2rayN/ServiceLib/Manager/GroupProfileManager.cs @@ -0,0 +1,175 @@ +namespace ServiceLib.Manager; + +public class GroupProfileManager +{ + public static async Task HasCycle(ProfileItem item) + { + return await HasCycle(item.IndexId, item.GetExtraItem()); + } + + public static async Task HasCycle(string? indexId, ProtocolExtraItem? extraInfo) + { + return await HasCycle(indexId, extraInfo, new HashSet(), new HashSet()); + } + + public static async Task HasCycle(string? indexId, ProtocolExtraItem? extraInfo, HashSet visited, HashSet stack) + { + if (indexId.IsNullOrEmpty() || extraInfo == null) + { + return false; + } + + if (stack.Contains(indexId)) + { + return true; + } + + if (visited.Contains(indexId)) + { + return false; + } + + visited.Add(indexId); + stack.Add(indexId); + + try + { + if (extraInfo.GroupType.IsNullOrEmpty()) + { + return false; + } + + if (extraInfo.ChildItems.IsNullOrEmpty()) + { + return false; + } + + var childIds = Utils.String2List(extraInfo.ChildItems) + ?.Where(p => !string.IsNullOrEmpty(p)) + .ToList(); + if (childIds == null) + { + return false; + } + + foreach (var child in childIds) + { + var childItem = await AppManager.Instance.GetProfileItem(child); + if (await HasCycle(child, childItem?.GetExtraItem(), visited, stack)) + { + return true; + } + } + + return false; + } + finally + { + stack.Remove(indexId); + } + } + + public static async Task<(List Items, ProtocolExtraItem? Extra)> GetChildProfileItems(ProfileItem profileItem) + { + var profileExtra = profileItem?.GetExtraItem(); + if (profileExtra == null) + { + return ([], null); + } + var items = await GetChildProfileItems(profileExtra); + var subItems = await GetSubChildProfileItems(profileExtra); + items.AddRange(subItems); + + return (items, profileExtra); + } + + public static async Task> GetChildProfileItems(ProtocolExtraItem? extra) + { + if (extra == null || extra.ChildItems.IsNullOrEmpty()) + { + return new(); + } + var childProfiles = (await Task.WhenAll( + Utils.String2List(extra.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> GetSubChildProfileItems(ProtocolExtraItem? extra) + { + if (extra == null || extra.Filter.IsNullOrEmpty()) + { + return new(); + } + var childProfiles = await AppManager.Instance.ProfileItems(extra.SubChildItems ?? string.Empty); + + return childProfiles.Where(p => + p != null && + p.IsValid() && + !p.ConfigType.IsComplexType() && + (extra.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, extra.Filter)) + ) + .ToList(); + } + + public static async Task> GetAllChildDomainAddresses(ProfileItem profileItem) + { + var childAddresses = new HashSet(); + var (childItems, _) = await GetChildProfileItems(profileItem); + foreach (var child in childItems) + { + if (!child.IsComplex()) + { + childAddresses.Add(child.Address); + } + else if (child.ConfigType.IsGroupType()) + { + var subAddresses = await GetAllChildDomainAddresses(child); + foreach (var addr in subAddresses) + { + childAddresses.Add(addr); + } + } + } + return childAddresses; + } + + public static async Task> GetAllChildEchQuerySni(ProfileItem profileItem) + { + var childAddresses = new HashSet(); + var (childItems, _) = await GetChildProfileItems(profileItem); + foreach (var childNode in childItems) + { + if (!childNode.IsComplex() && !childNode.EchConfigList.IsNullOrEmpty()) + { + if (childNode.StreamSecurity == Global.StreamSecurity + && childNode.EchConfigList?.Contains("://") == true) + { + var idx = childNode.EchConfigList.IndexOf('+'); + childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni); + } + else + { + childAddresses.Add(childNode.Sni); + } + } + else if (childNode.ConfigType.IsGroupType()) + { + var subAddresses = await GetAllChildDomainAddresses(childNode); + foreach (var addr in subAddresses) + { + childAddresses.Add(addr); + } + } + } + return childAddresses; + } +} diff --git a/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs b/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs index 041b1c78..01983440 100644 --- a/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs +++ b/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs @@ -29,11 +29,6 @@ public class ProfileGroupItemManager 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 IndexId not in ( select indexId from ProfileItem )"); @@ -42,347 +37,9 @@ public class ProfileGroupItemManager _items = new ConcurrentDictionary(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!)); } - private ProfileGroupItem AddProfileGroupItem(string indexId) - { - var profileGroupItem = new ProfileGroupItem() - { - IndexId = 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.IndexId)).ToDictionary(t => t.IndexId!); - - var lstInserts = new List(); - var lstUpdates = new List(); - - foreach (var item in _items.Values) - { - if (string.IsNullOrEmpty(item.IndexId)) - { - continue; - } - - if (existsMap.ContainsKey(item.IndexId)) - { - 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.IndexId)) - { - throw new ArgumentException("IndexId required", nameof(item)); - } - - _items[item.IndexId] = item; - - try - { - var lst = await SQLiteHelper.Instance.TableAsync().Where(t => t.IndexId == item.IndexId).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); - - try - { - 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(); - if (childIds == null) - { - return false; - } - - foreach (var child in childIds) - { - if (HasCycle(child, visited, stack)) - { - return true; - } - } - - return false; - } - finally - { - stack.Remove(indexId); - } - } - - public static async Task<(List Items, ProfileGroupItem? Group)> GetChildProfileItems(string? indexId) - { - Instance.TryGet(indexId, out var profileGroupItem); - if (profileGroupItem == null || profileGroupItem.NotHasChild()) - { - return (new List(), profileGroupItem); - } - - var items = new List(); - items.AddRange(await GetSubChildProfileItems(profileGroupItem)); - items.AddRange(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> GetSubChildProfileItems(ProfileGroupItem? group) - { - if (group == null || group.SubChildItems.IsNullOrEmpty()) - { - return new(); - } - var childProfiles = await AppManager.Instance.ProfileItems(group.SubChildItems); - - return childProfiles.Where(p => - p != null && - p.IsValid() && - !p.ConfigType.IsComplexType() && - (group.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, group.Filter)) - ) - .ToList(); - } - - public static async Task> GetAllChildDomainAddresses(string indexId) - { - // include grand children - var childAddresses = new HashSet(); - if (!Instance.TryGet(indexId, out var groupItem) || groupItem == null) - { - return childAddresses; - } - - if (groupItem.SubChildItems.IsNotEmpty()) - { - var subItems = await GetSubChildProfileItems(groupItem); - subItems.ForEach(p => childAddresses.Add(p.Address)); - } - - 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.IsGroupType()) - { - var subAddresses = await GetAllChildDomainAddresses(childNode.IndexId); - foreach (var addr in subAddresses) - { - childAddresses.Add(addr); - } - } - } - - return childAddresses; - } - - public static async Task> GetAllChildEchQuerySni(string indexId) - { - // include grand children - var childAddresses = new HashSet(); - if (!Instance.TryGet(indexId, out var groupItem) || groupItem == null) - { - return childAddresses; - } - - if (groupItem.SubChildItems.IsNotEmpty()) - { - var subItems = await GetSubChildProfileItems(groupItem); - foreach (var childNode in subItems) - { - if (childNode.EchConfigList.IsNullOrEmpty()) - { - continue; - } - if (childNode.StreamSecurity == Global.StreamSecurity - && childNode.EchConfigList?.Contains("://") == true) - { - var idx = childNode.EchConfigList.IndexOf('+'); - childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni); - } - else - { - childAddresses.Add(childNode.Sni); - } - } - } - - 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() && !childNode.EchConfigList.IsNullOrEmpty()) - { - if (childNode.StreamSecurity == Global.StreamSecurity - && childNode.EchConfigList?.Contains("://") == true) - { - var idx = childNode.EchConfigList.IndexOf('+'); - childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni); - } - else - { - childAddresses.Add(childNode.Sni); - } - } - else if (childNode.ConfigType.IsGroupType()) - { - 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 index 12c0f899..8d2f61b1 100644 --- a/v2rayN/ServiceLib/Models/ProfileGroupItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileGroupItem.cs @@ -1,5 +1,6 @@ namespace ServiceLib.Models; +// deprecated [Serializable] public class ProfileGroupItem { diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs index b5424265..b299ace0 100644 --- a/v2rayN/ServiceLib/Models/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileItem.cs @@ -94,7 +94,7 @@ public class ProfileItem : ReactiveObject return false; } - if (!Global.Flows.Contains(Flow)) + if (!Global.Flows.Contains(GetExtraItem().Flow ?? string.Empty)) { return false; } @@ -125,6 +125,20 @@ public class ProfileItem : ReactiveObject return true; } + public void SetExtraItem(ProtocolExtraItem extraItem) + { + JsonData = JsonUtils.Serialize(extraItem, false); + } + + public ProtocolExtraItem GetExtraItem() + { + if (JsonData.IsNullOrEmpty()) + { + return new ProtocolExtraItem(); + } + return JsonUtils.Deserialize(JsonData); + } + #endregion function [PrimaryKey] @@ -134,9 +148,7 @@ public class ProfileItem : ReactiveObject public int ConfigVersion { get; set; } public string Address { get; set; } public int Port { get; set; } - public string Ports { get; set; } public string Id { get; set; } - public int AlterId { get; set; } public string Security { get; set; } public string Network { get; set; } public string Remarks { get; set; } @@ -147,7 +159,6 @@ public class ProfileItem : ReactiveObject public string AllowInsecure { get; set; } public string Subid { get; set; } public bool IsSub { get; set; } = true; - public string Flow { get; set; } public string Sni { get; set; } public string Alpn { get; set; } = string.Empty; public ECoreType? CoreType { get; set; } @@ -164,4 +175,9 @@ public class ProfileItem : ReactiveObject public string CertSha { get; set; } public string EchConfigList { get; set; } public string EchForceQuery { get; set; } + public string JsonData { get; set; } + // deprecated + public string Ports { get; set; } + public int AlterId { get; set; } + public string Flow { get; set; } } diff --git a/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs new file mode 100644 index 00000000..41ba1c7d --- /dev/null +++ b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs @@ -0,0 +1,27 @@ +namespace ServiceLib.Models; + +public class ProtocolExtraItem +{ + // vmess + public string? AlterId { get; set; } + + // vless + public string? Flow { get; set; } + //public string? VisionSeed { get; set; } + + // shadowsocks + //public string? PluginArgs { get; set; } + + // hysteria2 + public string? UpMbps { get; set; } + public string? DownMbps { get; set; } + public string? Ports { get; set; } + public string? HopInterval { get; set; } + + // group profile + public string? GroupType { get; set; } + public string? ChildItems { get; set; } + public string? SubChildItems { get; set; } + public string? Filter { get; set; } + public EMultipleLoad? MultipleLoad { get; set; } +} diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs index 6079b2fb..ac304176 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs @@ -204,7 +204,7 @@ public partial class CoreConfigSingboxService } else if (node?.ConfigType.IsGroupType() == true) { - var queryServerNames = (await ProfileGroupItemManager.GetAllChildEchQuerySni(node.IndexId)).ToList(); + var queryServerNames = (await GroupProfileManager.GetAllChildEchQuerySni(node)).ToList(); if (queryServerNames.Count > 0) { singboxConfig.dns.rules.Add(new() diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index e2c0d502..61fe2f84 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -6,6 +6,7 @@ public partial class CoreConfigSingboxService { try { + var extraItem = node.GetExtraItem(); outbound.server = node.Address; outbound.server_port = node.Port; outbound.type = Global.ProtocolTypes[node.ConfigType]; @@ -15,7 +16,7 @@ public partial class CoreConfigSingboxService case EConfigType.VMess: { outbound.uuid = node.Id; - outbound.alter_id = node.AlterId; + outbound.alter_id = int.TryParse(extraItem?.AlterId, out var result) ? result : 0; if (Global.VmessSecurities.Contains(node.Security)) { outbound.security = node.Security; @@ -112,13 +113,13 @@ public partial class CoreConfigSingboxService outbound.packet_encoding = "xudp"; - if (node.Flow.IsNullOrEmpty()) + if (extraItem.Flow.IsNullOrEmpty()) { await GenOutboundMux(node, outbound); } else { - outbound.flow = node.Flow; + outbound.flow = extraItem.Flow; } await GenOutboundTransport(node, outbound); @@ -145,12 +146,14 @@ public partial class CoreConfigSingboxService }; } - outbound.up_mbps = _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null; - outbound.down_mbps = _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null; - if (node.Ports.IsNotEmpty() && (node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(','))) + var extra = node.GetExtraItem(); + outbound.up_mbps = int.TryParse(extra?.UpMbps, out var upMbps) && upMbps >= 0 ? upMbps : (_config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null); + outbound.down_mbps = int.TryParse(extra?.DownMbps, out var downMbps) && downMbps >= 0 ? downMbps : (_config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null); + var ports = extra?.Ports?.IsNullOrEmpty() == false ? extra.Ports : null; + if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) { outbound.server_port = null; - outbound.server_ports = node.Ports.Split(',') + outbound.server_ports = ports.Split(',') .Select(p => p.Trim()) .Where(p => p.IsNotEmpty()) .Select(p => @@ -453,13 +456,13 @@ public partial class CoreConfigSingboxService { return -1; } - var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId); + var hasCycle = await GroupProfileManager.HasCycle(node); if (hasCycle) { return -1; } - var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId); + var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node); if (childProfiles.Count <= 0) { return -1; @@ -467,13 +470,14 @@ public partial class CoreConfigSingboxService switch (node.ConfigType) { case EConfigType.PolicyGroup: + var multipleLoad = profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing; if (ignoreOriginChain) { - await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName); + await GenOutboundsList(childProfiles, singboxConfig, multipleLoad, baseTagName); } else { - await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName); + await GenOutboundsListWithChain(childProfiles, singboxConfig, multipleLoad, baseTagName); } break; @@ -585,7 +589,7 @@ public partial class CoreConfigSingboxService if (node.ConfigType.IsGroupType()) { - var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId); + var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node); if (childProfiles.Count <= 0) { continue; @@ -594,7 +598,8 @@ public partial class CoreConfigSingboxService var ret = node.ConfigType switch { EConfigType.PolicyGroup => - await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName), + await GenOutboundsListWithChain(childProfiles, singboxConfig, + profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing, childBaseTagName), EConfigType.ProxyChain => await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName), _ => throw new NotImplementedException() @@ -763,7 +768,7 @@ public partial class CoreConfigSingboxService if (node.ConfigType.IsGroupType()) { - var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId); + var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node); if (childProfiles.Count <= 0) { continue; @@ -772,7 +777,7 @@ public partial class CoreConfigSingboxService var ret = node.ConfigType switch { EConfigType.PolicyGroup => - await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName), + await GenOutboundsList(childProfiles, singboxConfig, profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing, childBaseTagName), EConfigType.ProxyChain => await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName), _ => throw new NotImplementedException() diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index d3575685..17fc7a09 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -6,6 +6,7 @@ public partial class CoreConfigV2rayService { try { + var extraItem = node.GetExtraItem(); var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled; switch (node.ConfigType) { @@ -36,7 +37,7 @@ public partial class CoreConfigV2rayService } usersItem.id = node.Id; - usersItem.alterId = node.AlterId; + usersItem.alterId = int.TryParse(extraItem?.AlterId, out var result) ? result : 0; usersItem.email = Global.UserEMail; if (Global.VmessSecurities.Contains(node.Security)) { @@ -142,13 +143,13 @@ public partial class CoreConfigV2rayService usersItem.email = Global.UserEMail; usersItem.encryption = node.Security; - if (node.Flow.IsNullOrEmpty()) + if (extraItem.Flow.IsNullOrEmpty()) { await GenOutboundMux(node, outbound, muxEnabled, muxEnabled); } else { - usersItem.flow = node.Flow; + usersItem.flow = extraItem.Flow; await GenOutboundMux(node, outbound, false, muxEnabled); } outbound.settings.servers = null; @@ -509,13 +510,15 @@ public partial class CoreConfigV2rayService break; case "hysteria": + var extraItem = node.GetExtraItem(); + var ports = extraItem?.Ports; HysteriaUdpHop4Ray? udpHop = null; - if (node.Ports.IsNotEmpty() && - (node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(','))) + if (!ports.IsNullOrEmpty() && + (ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) { udpHop = new() { - ports = node.Ports.Replace(':', '-'), + ports = ports.Replace(':', '-'), interval = _config.HysteriaItem.HopInterval > 0 ? _config.HysteriaItem.HopInterval : null, @@ -592,13 +595,13 @@ public partial class CoreConfigV2rayService { return -1; } - var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId); + var hasCycle = await GroupProfileManager.HasCycle(node); if (hasCycle) { return -1; } - var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId); + var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node); if (childProfiles.Count <= 0) { return -1; @@ -627,8 +630,9 @@ public partial class CoreConfigV2rayService //add balancers if (node.ConfigType == EConfigType.PolicyGroup) { - await GenObservatory(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName); - await GenBalancer(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName); + var multipleLoad = profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing; + await GenObservatory(v2rayConfig, multipleLoad, baseTagName); + await GenBalancer(v2rayConfig, multipleLoad, baseTagName); } } catch (Exception ex) @@ -737,7 +741,7 @@ public partial class CoreConfigV2rayService if (node.ConfigType.IsGroupType()) { - var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId); + var (childProfiles, _) = await GroupProfileManager.GetChildProfileItems(node); if (childProfiles.Count <= 0) { continue; @@ -891,7 +895,7 @@ public partial class CoreConfigV2rayService if (node.ConfigType.IsGroupType()) { - var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId); + var (childProfiles, _) = await GroupProfileManager.GetChildProfileItems(node); if (childProfiles.Count <= 0) { continue; diff --git a/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs index 5b0778a5..3789bafa 100644 --- a/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs @@ -79,8 +79,8 @@ public class AddGroupServerViewModel : MyReactiveObject public async Task Init() { - ProfileGroupItemManager.Instance.TryGet(SelectedSource.IndexId, out var profileGroup); - PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch + var extra = SelectedSource.GetExtraItem(); + PolicyGroupType = (extra?.MultipleLoad ?? EMultipleLoad.LeastPing) switch { EMultipleLoad.LeastPing => ResUI.TbLeastPing, EMultipleLoad.Fallback => ResUI.TbFallback, @@ -93,22 +93,18 @@ public class AddGroupServerViewModel : MyReactiveObject var subs = await AppManager.Instance.SubItems(); subs.Add(new SubItem()); SubItems.AddRange(subs); - SelectedSubItem = SubItems.Where(s => s.Id == profileGroup?.SubChildItems).FirstOrDefault(); - Filter = profileGroup?.Filter; + SelectedSubItem = SubItems.FirstOrDefault(s => s.Id == extra?.SubChildItems); + Filter = extra?.Filter; - var childItemMulti = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource?.IndexId); - if (childItemMulti != null) + var childIndexIds = Utils.String2List(extra?.ChildItems) ?? []; + foreach (var item in childIndexIds) { - var childIndexIds = Utils.String2List(childItemMulti.ChildItems) ?? []; - foreach (var item in childIndexIds) + var child = await AppManager.Instance.GetProfileItem(item); + if (child == null) { - var child = await AppManager.Instance.GetProfileItem(item); - if (child == null) - { - continue; - } - ChildItemsObs.Add(child); + continue; } + ChildItemsObs.Add(child); } } @@ -205,18 +201,11 @@ public class AddGroupServerViewModel : MyReactiveObject { return; } - var childIndexIds = new List(); - foreach (var item in ChildItemsObs) - { - if (item.IndexId.IsNullOrEmpty()) - { - continue; - } - childIndexIds.Add(item.IndexId); - } - var profileGroup = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource.IndexId); - profileGroup.ChildItems = Utils.List2String(childIndexIds); - profileGroup.MultipleLoad = PolicyGroupType switch + + var extra = SelectedSource.GetExtraItem(); + extra.ChildItems = + Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList()); + extra.MultipleLoad = PolicyGroupType switch { var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing, var s when s == ResUI.TbFallback => EMultipleLoad.Fallback, @@ -226,17 +215,19 @@ public class AddGroupServerViewModel : MyReactiveObject _ => EMultipleLoad.LeastPing, }; - profileGroup.SubChildItems = SelectedSubItem?.Id; - profileGroup.Filter = Filter; + extra.SubChildItems = SelectedSubItem?.Id; + extra.Filter = Filter; - var hasCycle = ProfileGroupItemManager.HasCycle(profileGroup.IndexId); + var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, extra); if (hasCycle) { NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks)); return; } - if (await ConfigHandler.AddGroupServerCommon(_config, SelectedSource, profileGroup, true) == 0) + SelectedSource.SetExtraItem(extra); + + if (await ConfigHandler.AddServerCommon(_config, SelectedSource) == 0) { NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); _updateView?.Invoke(EViewAction.CloseWindow, null); diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index 775274d7..589aa8df 100644 --- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -17,6 +17,15 @@ public class AddServerViewModel : MyReactiveObject [Reactive] public string CertSha { get; set; } + [Reactive] + public int AlterId { get; set; } + + [Reactive] + public string Ports { get; set; } + + [Reactive] + public string Flow { get; set; } + public ReactiveCommand FetchCertCmd { get; } public ReactiveCommand FetchCertChainCmd { get; } public ReactiveCommand SaveCmd { get; } @@ -63,6 +72,11 @@ public class AddServerViewModel : MyReactiveObject CoreType = SelectedSource?.CoreType?.ToString(); Cert = SelectedSource?.Cert?.ToString() ?? string.Empty; CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty; + + var extraItem = SelectedSource?.GetExtraItem(); + Ports = extraItem?.Ports ?? string.Empty; + AlterId = int.TryParse(extraItem?.AlterId, out var result) ? result : 0; + Flow = extraItem?.Flow ?? string.Empty; } private async Task SaveServerAsync() @@ -109,6 +123,11 @@ public class AddServerViewModel : MyReactiveObject SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType); SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert; SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha; + var extraItem = SelectedSource.GetExtraItem(); + extraItem.Ports = Ports; + extraItem.AlterId = AlterId > 0 ? AlterId.ToString() : string.Empty; + extraItem.Flow = Flow; + SelectedSource.SetExtraItem(extraItem); if (await ConfigHandler.AddServer(_config, SelectedSource) == 0) { diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index 9220e8ae..74610bae 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -120,7 +120,7 @@ public partial class AddServerWindow : WindowBase { case EConfigType.VMess: this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables); break; @@ -139,21 +139,21 @@ public partial class AddServerWindow : WindowBase case EConfigType.VLESS: this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables); break; case EConfigType.Trojan: this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables); break; case EConfigType.Hysteria2: this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); break; case EConfigType.TUIC: diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index 1136220d..f353eb9c 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -115,7 +115,7 @@ public partial class AddServerWindow { case EConfigType.VMess: this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables); break; @@ -134,21 +134,21 @@ public partial class AddServerWindow case EConfigType.VLESS: this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow5.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables); break; case EConfigType.Trojan: this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow6.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables); break; case EConfigType.Hysteria2: this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); break; case EConfigType.TUIC: From 1c2d086a71940709d4c5cb4d6713ae6793f74dfb Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 17 Jan 2026 19:05:50 +0800 Subject: [PATCH 02/20] Add hysteria2 bandwidth and hop interval support --- v2rayN/ServiceLib/Global.cs | 2 + v2rayN/ServiceLib/Models/ProtocolExtraItem.cs | 6 +-- v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 9 ++++ v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.fr.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.hu.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.ru.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 3 ++ .../Singbox/SingboxOutboundService.cs | 15 ++++--- .../CoreConfig/V2ray/V2rayOutboundService.cs | 19 +++++--- .../ViewModels/AddServerViewModel.cs | 15 +++++++ .../Views/AddServerWindow.axaml | 37 +++++++++++++++- .../Views/AddServerWindow.axaml.cs | 3 ++ v2rayN/v2rayN/Views/AddServerWindow.xaml | 43 +++++++++++++++++++ v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 3 ++ 17 files changed, 158 insertions(+), 15 deletions(-) diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 486e53ed..e502435f 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -90,6 +90,8 @@ public class Global public const string SingboxFakeDNSTag = "fake_dns"; public const string SingboxEchDNSTag = "ech_dns"; + public const int Hysteria2DefaultHopInt = 10; + public static readonly List IEProxyProtocols = [ "{ip}:{http_port}", diff --git a/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs index 41ba1c7d..bfebedbe 100644 --- a/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs +++ b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs @@ -13,10 +13,10 @@ public class ProtocolExtraItem //public string? PluginArgs { get; set; } // hysteria2 - public string? UpMbps { get; set; } - public string? DownMbps { get; set; } + public int? UpMbps { get; set; } + public int? DownMbps { get; set; } public string? Ports { get; set; } - public string? HopInterval { get; set; } + public int? HopInterval { get; set; } // group profile public string? GroupType { get; set; } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index fd3b2d9f..46de8983 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -2997,6 +2997,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Port hopping interval 的本地化字符串。 + /// + public static string TbHopInt7 { + get { + return ResourceManager.GetString("TbHopInt7", resourceCulture); + } + } + /// /// 查找类似 UUID(id) 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 16fe442f..fdcffe9b 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index a114ac21..5db657ca 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1662,4 +1662,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 21e4e68d..9e5c9c4d 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 91095de4..dfd744a3 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index adc9cc98..67cffea7 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ 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 b1675d48..f17495fd 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1662,4 +1662,7 @@ 当未选择或 "AsIs" 时,由远程服务器端 DNS 解析;否则,使用内部 DNS 模块解析。 + + 端口跳跃间隔 + \ 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 73a7dfe5..160340d1 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1662,4 +1662,7 @@ If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 61fe2f84..ccf62db2 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -146,10 +146,13 @@ public partial class CoreConfigSingboxService }; } - var extra = node.GetExtraItem(); - outbound.up_mbps = int.TryParse(extra?.UpMbps, out var upMbps) && upMbps >= 0 ? upMbps : (_config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null); - outbound.down_mbps = int.TryParse(extra?.DownMbps, out var downMbps) && downMbps >= 0 ? downMbps : (_config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null); - var ports = extra?.Ports?.IsNullOrEmpty() == false ? extra.Ports : null; + outbound.up_mbps = extraItem?.UpMbps is { } su and >= 0 + ? su + : _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null; + outbound.down_mbps = extraItem?.DownMbps is { } sd and >= 0 + ? sd + : _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null; + var ports = extraItem?.Ports?.IsNullOrEmpty() == false ? extraItem.Ports : null; if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) { outbound.server_port = null; @@ -162,7 +165,9 @@ public partial class CoreConfigSingboxService return port.Contains(':') ? port : $"{port}:{port}"; }) .ToList(); - outbound.hop_interval = _config.HysteriaItem.HopInterval > 0 ? $"{_config.HysteriaItem.HopInterval}s" : null; + outbound.hop_interval = extraItem?.HopInterval is { } hi and >= 5 + ? $"{hi}s" + : _config.HysteriaItem.HopInterval >= 5 ? $"{_config.HysteriaItem.HopInterval}s" : $"{Global.Hysteria2DefaultHopInt}s"; } break; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index 17fc7a09..3d42fb67 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -512,24 +512,31 @@ public partial class CoreConfigV2rayService case "hysteria": var extraItem = node.GetExtraItem(); var ports = extraItem?.Ports; + int? upMbps = extraItem?.UpMbps is { } su and >= 0 + ? su + : _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null; + int? downMbps = extraItem?.DownMbps is { } sd and >= 0 + ? sd + : _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null; + var hopInterval = extraItem?.HopInterval is { } hi and >= 5 + ? hi + : _config.HysteriaItem.HopInterval >= 5 ? _config.HysteriaItem.HopInterval : Global.Hysteria2DefaultHopInt; HysteriaUdpHop4Ray? udpHop = null; if (!ports.IsNullOrEmpty() && (ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) { - udpHop = new() + udpHop = new HysteriaUdpHop4Ray { ports = ports.Replace(':', '-'), - interval = _config.HysteriaItem.HopInterval > 0 - ? _config.HysteriaItem.HopInterval - : null, + interval = hopInterval, }; } HysteriaSettings4Ray hysteriaSettings = new() { version = 2, auth = node.Id, - up = _config.HysteriaItem.UpMbps > 0 ? $"{_config.HysteriaItem.UpMbps}mbps" : null, - down = _config.HysteriaItem.DownMbps > 0 ? $"{_config.HysteriaItem.DownMbps}mbps" : null, + up = upMbps > 0 ? $"{upMbps}mbps" : null, + down = downMbps > 0 ? $"{downMbps}mbps" : null, udphop = udpHop, }; streamSettings.hysteriaSettings = hysteriaSettings; diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index 589aa8df..2f629c79 100644 --- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -23,6 +23,15 @@ public class AddServerViewModel : MyReactiveObject [Reactive] public string Ports { get; set; } + [Reactive] + public int UpMbps { get; set; } + + [Reactive] + public int DownMbps { get; set; } + + [Reactive] + public int HopInterval { get; set; } + [Reactive] public string Flow { get; set; } @@ -77,6 +86,9 @@ public class AddServerViewModel : MyReactiveObject Ports = extraItem?.Ports ?? string.Empty; AlterId = int.TryParse(extraItem?.AlterId, out var result) ? result : 0; Flow = extraItem?.Flow ?? string.Empty; + UpMbps = extraItem?.UpMbps ?? 0; + DownMbps = extraItem?.DownMbps ?? 0; + HopInterval = extraItem?.HopInterval ?? Global.Hysteria2DefaultHopInt; } private async Task SaveServerAsync() @@ -127,6 +139,9 @@ public class AddServerViewModel : MyReactiveObject extraItem.Ports = Ports; extraItem.AlterId = AlterId > 0 ? AlterId.ToString() : string.Empty; extraItem.Flow = Flow; + extraItem.UpMbps = UpMbps; + extraItem.DownMbps = DownMbps; + extraItem.HopInterval = HopInterval >= 5 ? HopInterval : Global.Hysteria2DefaultHopInt; SelectedSource.SetExtraItem(extraItem); if (await ConfigHandler.AddServer(_config, SelectedSource) == 0) diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index 19c62868..356698fb 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -360,7 +360,7 @@ Grid.Row="2" ColumnDefinitions="300,Auto,Auto" IsVisible="False" - RowDefinitions="Auto,Auto,Auto,Auto"> + RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto"> + + + + + + + + + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(disposables); break; case EConfigType.TUIC: diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml b/v2rayN/v2rayN/Views/AddServerWindow.xaml index 1f70489b..8c6b4a35 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml @@ -487,6 +487,8 @@ + + @@ -547,6 +549,47 @@ VerticalAlignment="Center" Style="{StaticResource ToolbarTextBlock}" Text="{x:Static resx:ResUI.TbPorts7Tips}" /> + + + + + + + + + vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(disposables); break; case EConfigType.TUIC: From 2d450357d1f49ec9824a5d958e86beafda5298db Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 19 Jan 2026 13:24:42 +0800 Subject: [PATCH 03/20] Upgrade config version and rename --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 20 +++--- v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs | 12 ++-- v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs | 8 +-- v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs | 10 +-- .../Manager/ActionPrecheckManager.cs | 6 +- v2rayN/ServiceLib/Manager/AppManager.cs | 62 ++++++++++++------- .../ServiceLib/Manager/GroupProfileManager.cs | 16 ++--- .../Manager/ProfileGroupItemManager.cs | 1 + v2rayN/ServiceLib/Models/ProfileGroupItem.cs | 2 +- v2rayN/ServiceLib/Models/ProfileItem.cs | 28 +++++---- v2rayN/ServiceLib/Models/ProtocolExtraItem.cs | 13 ++++ .../Singbox/SingboxOutboundService.cs | 16 ++--- .../CoreConfig/V2ray/V2rayOutboundService.cs | 18 +++--- .../ViewModels/AddGroupServerViewModel.cs | 24 +++---- .../ViewModels/AddServerViewModel.cs | 30 ++++----- 15 files changed, 152 insertions(+), 114 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index b5fdea2b..2158f88a 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -237,6 +237,8 @@ public static class ConfigHandler item.Security = profileItem.Security; item.Flow = profileItem.Flow; + item.Password = profileItem.Password; + item.Network = profileItem.Network; item.HeaderType = profileItem.HeaderType; item.RequestHost = profileItem.RequestHost; @@ -258,7 +260,7 @@ public static class ConfigHandler item.CertSha = profileItem.CertSha; item.EchConfigList = profileItem.EchConfigList; item.EchForceQuery = profileItem.EchForceQuery; - item.JsonData = profileItem.JsonData; + item.ProtoExtra = profileItem.ProtoExtra; } var ret = item.ConfigType switch @@ -963,11 +965,11 @@ public static class ConfigHandler profileItem.Path = profileItem.Path.TrimEx(); profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx(); - var extraItem = profileItem.GetExtraItem(); + var protocolExtra = profileItem.GetProtocolExtra(); - if (!Global.Flows.Contains(extraItem.Flow ?? string.Empty)) + if (!Global.Flows.Contains(protocolExtra.Flow ?? string.Empty)) { - extraItem.Flow = Global.Flows.First(); + protocolExtra.Flow = Global.Flows.First(); } if (profileItem.Id.IsNullOrEmpty()) { @@ -978,7 +980,7 @@ public static class ConfigHandler profileItem.Security = Global.None; } - profileItem.SetExtraItem(extraItem); + profileItem.SetProtocolExtra(protocolExtra); await AddServerCommon(config, profileItem, toFile); @@ -1033,7 +1035,7 @@ public static class ConfigHandler /// 0 if successful public static async Task AddServerCommon(Config config, ProfileItem profileItem, bool toFile = true) { - profileItem.ConfigVersion = 2; + profileItem.ConfigVersion = 3; if (profileItem.StreamSecurity.IsNotEmpty()) { @@ -1097,7 +1099,7 @@ public static class ConfigHandler return false; } - var extraItem = o.GetExtraItem(); + var protocolExtra = o.GetProtocolExtra(); return o.ConfigType == n.ConfigType && AreEqual(o.Address, n.Address) @@ -1109,7 +1111,7 @@ public static class ConfigHandler && AreEqual(o.RequestHost, n.RequestHost) && AreEqual(o.Path, n.Path) && (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity) - && AreEqual(extraItem.Flow, extraItem.Flow) + && AreEqual(protocolExtra.Flow, protocolExtra.Flow) && AreEqual(o.Sni, n.Sni) && AreEqual(o.Alpn, n.Alpn) && AreEqual(o.Fingerprint, n.Fingerprint) @@ -1208,7 +1210,7 @@ public static class ConfigHandler { ChildItems = childProfileIndexId, MultipleLoad = multipleLoad, }; - profile.SetExtraItem(extraItem); + profile.SetProtocolExtra(extraItem); var ret = await AddServerCommon(config, profile, true); result.Success = ret == 0; result.Data = indexId; diff --git a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs index a2930362..1ff712b7 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs @@ -21,10 +21,10 @@ public class BaseFmt protected static int ToUriQuery(ProfileItem item, string? securityDef, ref Dictionary dicQuery) { - var extraItem = item.GetExtraItem(); - if (!extraItem.Flow.IsNullOrEmpty()) + var protocolExtra = item.GetProtocolExtra(); + if (!protocolExtra.Flow.IsNullOrEmpty()) { - dicQuery.Add("flow", extraItem.Flow); + dicQuery.Add("flow", protocolExtra.Flow); } if (item.StreamSecurity.IsNotEmpty()) @@ -209,9 +209,9 @@ public class BaseFmt protected static int ResolveUriQuery(NameValueCollection query, ref ProfileItem item) { - var extraItem = item.GetExtraItem(); - extraItem.Flow = GetQueryValue(query, "flow"); - item.SetExtraItem(extraItem); + var protocolExtra = item.GetProtocolExtra(); + protocolExtra.Flow = GetQueryValue(query, "flow"); + item.SetProtocolExtra(protocolExtra); item.StreamSecurity = GetQueryValue(query, "security"); item.Sni = GetQueryValue(query, "sni"); item.Alpn = GetQueryDecoded(query, "alpn"); diff --git a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs index fe6f9168..236643d1 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs @@ -32,7 +32,7 @@ public class Hysteria2Fmt : BaseFmt { Ports = GetQueryDecoded(query, "mport") }; - item.SetExtraItem(extraItem); + item.SetProtocolExtra(extraItem); return item; } @@ -59,10 +59,10 @@ public class Hysteria2Fmt : BaseFmt dicQuery.Add("obfs", "salamander"); dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path)); } - var extra = item.GetExtraItem(); - if (extra?.Ports?.IsNotEmpty() ?? false) + var protocolExtra = item.GetProtocolExtra(); + if (protocolExtra?.Ports?.IsNotEmpty() ?? false) { - dicQuery.Add("mport", Utils.UrlEncode(extra.Ports.Replace(':', '-'))); + dicQuery.Add("mport", Utils.UrlEncode(protocolExtra.Ports.Replace(':', '-'))); } if (!item.CertSha.IsNullOrEmpty()) { diff --git a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs index 6bab8a50..52438508 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs @@ -24,15 +24,15 @@ public class VmessFmt : BaseFmt return null; } - var extraItem = item?.GetExtraItem(); + var protocolExtra = item?.GetProtocolExtra(); var vmessQRCode = new VmessQRCode { - v = item.ConfigVersion, + v = 2, ps = item.Remarks.TrimEx(), add = item.Address, port = item.Port, id = item.Id, - aid = int.TryParse(extraItem?.AlterId, out var result) ? result : 0, + aid = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0, scy = item.Security, net = item.Network, type = item.HeaderType, @@ -73,12 +73,12 @@ public class VmessFmt : BaseFmt item.Network = Global.DefaultNetwork; item.HeaderType = Global.None; - item.ConfigVersion = vmessQRCode.v; + //item.ConfigVersion = vmessQRCode.v; item.Remarks = Utils.ToString(vmessQRCode.ps); item.Address = Utils.ToString(vmessQRCode.add); item.Port = vmessQRCode.port; item.Id = Utils.ToString(vmessQRCode.id); - item.SetExtraItem(new ProtocolExtraItem + item.SetProtocolExtra(new ProtocolExtraItem { AlterId = vmessQRCode.aid.ToString(), }); diff --git a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs index e101ba53..78c78ba0 100644 --- a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs +++ b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs @@ -128,7 +128,7 @@ public class ActionPrecheckManager } } - var extraItem = item.GetExtraItem(); + var protocolExtra = item.GetProtocolExtra(); switch (item.ConfigType) { @@ -146,7 +146,7 @@ public class ActionPrecheckManager errors.Add(string.Format(ResUI.InvalidProperty, "Id")); } - if (!Global.Flows.Contains(extraItem.Flow ?? string.Empty)) + if (!Global.Flows.Contains(protocolExtra.Flow ?? string.Empty)) { errors.Add(string.Format(ResUI.InvalidProperty, "Flow")); } @@ -204,7 +204,7 @@ public class ActionPrecheckManager { var errors = new List(); - var hasCycle = await GroupProfileManager.HasCycle(item.IndexId, item.GetExtraItem()); + var hasCycle = await GroupProfileManager.HasCycle(item.IndexId, item.GetProtocolExtra()); if (hasCycle) { errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks)); diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 4c978934..2acf9246 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -81,7 +81,9 @@ public sealed class AppManager SQLiteHelper.Instance.CreateTable(); SQLiteHelper.Instance.CreateTable(); SQLiteHelper.Instance.CreateTable(); +#pragma warning disable CS0618 SQLiteHelper.Instance.CreateTable(); +#pragma warning restore CS0618 return true; } @@ -259,36 +261,54 @@ public sealed class AppManager public async Task MigrateProfileExtra() { - var list = await SQLiteHelper.Instance.TableAsync().ToListAsync(); - foreach (var item in list) +#pragma warning disable CS0618 + const int pageSize = 500; + int offset = 0; + + while (true) { - if (!item.JsonData.IsNullOrEmpty()) + var sql = $"SELECT * FROM ProfileItem WHERE ConfigVersion < 3 LIMIT {pageSize} OFFSET {offset}"; + var batch = await SQLiteHelper.Instance.QueryAsync(sql); + if (batch is null || batch.Count == 0) { - return; + break; } - ProtocolExtraItem extra = new() + + foreach (var item in batch) { - AlterId = item.AlterId.ToString(), - Flow = item.Flow.IsNotEmpty() ? item.Flow : null, - Ports = item.Ports.IsNotEmpty() ? item.Ports : null, - }; - if (item.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain) - { - extra.GroupType = nameof(item.ConfigType); - ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var groupItem); - if (groupItem != null && !groupItem.NotHasChild()) + ProtocolExtraItem extra = new() { - extra.ChildItems = groupItem.ChildItems; - extra.SubChildItems = groupItem.SubChildItems; - extra.Filter = groupItem.Filter; - extra.MultipleLoad = groupItem.MultipleLoad; + AlterId = item.AlterId.ToString(), + Flow = item.Flow.IsNotEmpty() ? item.Flow : null, + Ports = item.Ports.IsNotEmpty() ? item.Ports : null, + }; + + if (item.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain) + { + extra.GroupType = nameof(item.ConfigType); + ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var groupItem); + if (groupItem != null && !groupItem.NotHasChild()) + { + extra.ChildItems = groupItem.ChildItems; + extra.SubChildItems = groupItem.SubChildItems; + extra.Filter = groupItem.Filter; + extra.MultipleLoad = groupItem.MultipleLoad; + } } + + item.SetProtocolExtra(extra); + + item.Password = item.Id; + + item.ConfigVersion = 3; + await SQLiteHelper.Instance.UpdateAsync(item); } - item.SetExtraItem(extra); - await SQLiteHelper.Instance.UpdateAsync(item); + + offset += pageSize; } - await ProfileGroupItemManager.Instance.ClearAll(); + //await ProfileGroupItemManager.Instance.ClearAll(); +#pragma warning restore CS0618 } #endregion SqliteHelper diff --git a/v2rayN/ServiceLib/Manager/GroupProfileManager.cs b/v2rayN/ServiceLib/Manager/GroupProfileManager.cs index af2029fa..ce7341dc 100644 --- a/v2rayN/ServiceLib/Manager/GroupProfileManager.cs +++ b/v2rayN/ServiceLib/Manager/GroupProfileManager.cs @@ -4,7 +4,7 @@ public class GroupProfileManager { public static async Task HasCycle(ProfileItem item) { - return await HasCycle(item.IndexId, item.GetExtraItem()); + return await HasCycle(item.IndexId, item.GetProtocolExtra()); } public static async Task HasCycle(string? indexId, ProtocolExtraItem? extraInfo) @@ -55,7 +55,7 @@ public class GroupProfileManager foreach (var child in childIds) { var childItem = await AppManager.Instance.GetProfileItem(child); - if (await HasCycle(child, childItem?.GetExtraItem(), visited, stack)) + if (await HasCycle(child, childItem?.GetProtocolExtra(), visited, stack)) { return true; } @@ -71,16 +71,12 @@ public class GroupProfileManager public static async Task<(List Items, ProtocolExtraItem? Extra)> GetChildProfileItems(ProfileItem profileItem) { - var profileExtra = profileItem?.GetExtraItem(); - if (profileExtra == null) - { - return ([], null); - } - var items = await GetChildProfileItems(profileExtra); - var subItems = await GetSubChildProfileItems(profileExtra); + var protocolExtra = profileItem?.GetProtocolExtra(); + var items = await GetChildProfileItems(protocolExtra); + var subItems = await GetSubChildProfileItems(protocolExtra); items.AddRange(subItems); - return (items, profileExtra); + return (items, protocolExtra); } public static async Task> GetChildProfileItems(ProtocolExtraItem? extra) diff --git a/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs b/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs index 01983440..97b037d1 100644 --- a/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs +++ b/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs @@ -1,5 +1,6 @@ namespace ServiceLib.Manager; +[Obsolete("Use GroupProfileManager instead.")] public class ProfileGroupItemManager { private static readonly Lazy _instance = new(() => new()); diff --git a/v2rayN/ServiceLib/Models/ProfileGroupItem.cs b/v2rayN/ServiceLib/Models/ProfileGroupItem.cs index 8d2f61b1..94a9aad2 100644 --- a/v2rayN/ServiceLib/Models/ProfileGroupItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileGroupItem.cs @@ -1,6 +1,6 @@ namespace ServiceLib.Models; -// deprecated +[Obsolete("Use ProtocolExtraItem instead.")] [Serializable] public class ProfileGroupItem { diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs index b299ace0..478754bc 100644 --- a/v2rayN/ServiceLib/Models/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileItem.cs @@ -7,9 +7,10 @@ public class ProfileItem : ReactiveObject { IndexId = string.Empty; ConfigType = EConfigType.VMess; - ConfigVersion = 2; + ConfigVersion = 3; Address = string.Empty; Port = 0; + Password = string.Empty; Id = string.Empty; AlterId = 0; Security = string.Empty; @@ -94,7 +95,7 @@ public class ProfileItem : ReactiveObject return false; } - if (!Global.Flows.Contains(GetExtraItem().Flow ?? string.Empty)) + if (!Global.Flows.Contains(GetProtocolExtra().Flow ?? string.Empty)) { return false; } @@ -125,18 +126,18 @@ public class ProfileItem : ReactiveObject return true; } - public void SetExtraItem(ProtocolExtraItem extraItem) + public void SetProtocolExtra(ProtocolExtraItem extraItem) { - JsonData = JsonUtils.Serialize(extraItem, false); + ProtoExtra = JsonUtils.Serialize(extraItem, false); } - public ProtocolExtraItem GetExtraItem() + public ProtocolExtraItem GetProtocolExtra() { - if (JsonData.IsNullOrEmpty()) + if (ProtoExtra.IsNullOrEmpty()) { return new ProtocolExtraItem(); } - return JsonUtils.Deserialize(JsonData); + return JsonUtils.Deserialize(ProtoExtra); } #endregion function @@ -148,8 +149,7 @@ public class ProfileItem : ReactiveObject public int ConfigVersion { get; set; } public string Address { get; set; } public int Port { get; set; } - public string Id { get; set; } - public string Security { get; set; } + public string Password { get; set; } public string Network { get; set; } public string Remarks { get; set; } public string HeaderType { get; set; } @@ -175,9 +175,15 @@ public class ProfileItem : ReactiveObject public string CertSha { get; set; } public string EchConfigList { get; set; } public string EchForceQuery { get; set; } - public string JsonData { get; set; } - // deprecated + public string ProtoExtra { get; set; } + [Obsolete("Use ProtocolExtraItem.Ports instead.")] public string Ports { get; set; } + [Obsolete("Use ProtocolExtraItem.AlterId instead.")] public int AlterId { get; set; } + [Obsolete("Use ProtocolExtraItem.Flow instead.")] public string Flow { get; set; } + [Obsolete("Use ProfileItem.Password instead.")] + public string Id { get; set; } + [Obsolete("Use ProtocolExtraItem.xxx instead.")] + public string Security { get; set; } } diff --git a/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs index bfebedbe..eea4698c 100644 --- a/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs +++ b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs @@ -4,13 +4,26 @@ public class ProtocolExtraItem { // vmess public string? AlterId { get; set; } + public string? VmessSecurity { get; set; } // vless public string? Flow { get; set; } + public string? VlessEncryption { get; set; } //public string? VisionSeed { get; set; } // shadowsocks //public string? PluginArgs { get; set; } + public string? SsMethod { get; set; } + + // socks and http + public string? Username { get; set; } + + // wireguard + public string? WgPublicKey { get; set; } + public string? WgPresharedKey { get; set; } + public string? WgInterfaceAddress { get; set; } + public string? WgReserved { get; set; } + public int? WgMtu { get; set; } // hysteria2 public int? UpMbps { get; set; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index ccf62db2..edc17a16 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -6,7 +6,7 @@ public partial class CoreConfigSingboxService { try { - var extraItem = node.GetExtraItem(); + var protocolExtra = node.GetProtocolExtra(); outbound.server = node.Address; outbound.server_port = node.Port; outbound.type = Global.ProtocolTypes[node.ConfigType]; @@ -16,7 +16,7 @@ public partial class CoreConfigSingboxService case EConfigType.VMess: { outbound.uuid = node.Id; - outbound.alter_id = int.TryParse(extraItem?.AlterId, out var result) ? result : 0; + outbound.alter_id = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0; if (Global.VmessSecurities.Contains(node.Security)) { outbound.security = node.Security; @@ -113,13 +113,13 @@ public partial class CoreConfigSingboxService outbound.packet_encoding = "xudp"; - if (extraItem.Flow.IsNullOrEmpty()) + if (protocolExtra.Flow.IsNullOrEmpty()) { await GenOutboundMux(node, outbound); } else { - outbound.flow = extraItem.Flow; + outbound.flow = protocolExtra.Flow; } await GenOutboundTransport(node, outbound); @@ -146,13 +146,13 @@ public partial class CoreConfigSingboxService }; } - outbound.up_mbps = extraItem?.UpMbps is { } su and >= 0 + outbound.up_mbps = protocolExtra?.UpMbps is { } su and >= 0 ? su : _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null; - outbound.down_mbps = extraItem?.DownMbps is { } sd and >= 0 + outbound.down_mbps = protocolExtra?.DownMbps is { } sd and >= 0 ? sd : _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null; - var ports = extraItem?.Ports?.IsNullOrEmpty() == false ? extraItem.Ports : null; + var ports = protocolExtra?.Ports?.IsNullOrEmpty() == false ? protocolExtra.Ports : null; if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) { outbound.server_port = null; @@ -165,7 +165,7 @@ public partial class CoreConfigSingboxService return port.Contains(':') ? port : $"{port}:{port}"; }) .ToList(); - outbound.hop_interval = extraItem?.HopInterval is { } hi and >= 5 + outbound.hop_interval = protocolExtra?.HopInterval is { } hi and >= 5 ? $"{hi}s" : _config.HysteriaItem.HopInterval >= 5 ? $"{_config.HysteriaItem.HopInterval}s" : $"{Global.Hysteria2DefaultHopInt}s"; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index 3d42fb67..7ff29768 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -6,7 +6,7 @@ public partial class CoreConfigV2rayService { try { - var extraItem = node.GetExtraItem(); + var protocolExtra = node.GetProtocolExtra(); var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled; switch (node.ConfigType) { @@ -37,7 +37,7 @@ public partial class CoreConfigV2rayService } usersItem.id = node.Id; - usersItem.alterId = int.TryParse(extraItem?.AlterId, out var result) ? result : 0; + usersItem.alterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0; usersItem.email = Global.UserEMail; if (Global.VmessSecurities.Contains(node.Security)) { @@ -143,13 +143,13 @@ public partial class CoreConfigV2rayService usersItem.email = Global.UserEMail; usersItem.encryption = node.Security; - if (extraItem.Flow.IsNullOrEmpty()) + if (protocolExtra.Flow.IsNullOrEmpty()) { await GenOutboundMux(node, outbound, muxEnabled, muxEnabled); } else { - usersItem.flow = extraItem.Flow; + usersItem.flow = protocolExtra.Flow; await GenOutboundMux(node, outbound, false, muxEnabled); } outbound.settings.servers = null; @@ -510,15 +510,15 @@ public partial class CoreConfigV2rayService break; case "hysteria": - var extraItem = node.GetExtraItem(); - var ports = extraItem?.Ports; - int? upMbps = extraItem?.UpMbps is { } su and >= 0 + var protocolExtra = node.GetProtocolExtra(); + var ports = protocolExtra?.Ports; + int? upMbps = protocolExtra?.UpMbps is { } su and >= 0 ? su : _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null; - int? downMbps = extraItem?.DownMbps is { } sd and >= 0 + int? downMbps = protocolExtra?.DownMbps is { } sd and >= 0 ? sd : _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null; - var hopInterval = extraItem?.HopInterval is { } hi and >= 5 + var hopInterval = protocolExtra?.HopInterval is { } hi and >= 5 ? hi : _config.HysteriaItem.HopInterval >= 5 ? _config.HysteriaItem.HopInterval : Global.Hysteria2DefaultHopInt; HysteriaUdpHop4Ray? udpHop = null; diff --git a/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs index 3789bafa..0bedeac5 100644 --- a/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs @@ -79,8 +79,8 @@ public class AddGroupServerViewModel : MyReactiveObject public async Task Init() { - var extra = SelectedSource.GetExtraItem(); - PolicyGroupType = (extra?.MultipleLoad ?? EMultipleLoad.LeastPing) switch + var protocolExtra = SelectedSource.GetProtocolExtra(); + PolicyGroupType = (protocolExtra?.MultipleLoad ?? EMultipleLoad.LeastPing) switch { EMultipleLoad.LeastPing => ResUI.TbLeastPing, EMultipleLoad.Fallback => ResUI.TbFallback, @@ -93,10 +93,10 @@ public class AddGroupServerViewModel : MyReactiveObject var subs = await AppManager.Instance.SubItems(); subs.Add(new SubItem()); SubItems.AddRange(subs); - SelectedSubItem = SubItems.FirstOrDefault(s => s.Id == extra?.SubChildItems); - Filter = extra?.Filter; + SelectedSubItem = SubItems.FirstOrDefault(s => s.Id == protocolExtra?.SubChildItems); + Filter = protocolExtra?.Filter; - var childIndexIds = Utils.String2List(extra?.ChildItems) ?? []; + var childIndexIds = Utils.String2List(protocolExtra?.ChildItems) ?? []; foreach (var item in childIndexIds) { var child = await AppManager.Instance.GetProfileItem(item); @@ -202,10 +202,10 @@ public class AddGroupServerViewModel : MyReactiveObject return; } - var extra = SelectedSource.GetExtraItem(); - extra.ChildItems = + var protocolExtra = SelectedSource.GetProtocolExtra(); + protocolExtra.ChildItems = Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList()); - extra.MultipleLoad = PolicyGroupType switch + protocolExtra.MultipleLoad = PolicyGroupType switch { var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing, var s when s == ResUI.TbFallback => EMultipleLoad.Fallback, @@ -215,17 +215,17 @@ public class AddGroupServerViewModel : MyReactiveObject _ => EMultipleLoad.LeastPing, }; - extra.SubChildItems = SelectedSubItem?.Id; - extra.Filter = Filter; + protocolExtra.SubChildItems = SelectedSubItem?.Id; + protocolExtra.Filter = Filter; - var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, extra); + var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, protocolExtra); if (hasCycle) { NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks)); return; } - SelectedSource.SetExtraItem(extra); + SelectedSource.SetProtocolExtra(protocolExtra); if (await ConfigHandler.AddServerCommon(_config, SelectedSource) == 0) { diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index 2f629c79..99a52c12 100644 --- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -82,13 +82,13 @@ public class AddServerViewModel : MyReactiveObject Cert = SelectedSource?.Cert?.ToString() ?? string.Empty; CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty; - var extraItem = SelectedSource?.GetExtraItem(); - Ports = extraItem?.Ports ?? string.Empty; - AlterId = int.TryParse(extraItem?.AlterId, out var result) ? result : 0; - Flow = extraItem?.Flow ?? string.Empty; - UpMbps = extraItem?.UpMbps ?? 0; - DownMbps = extraItem?.DownMbps ?? 0; - HopInterval = extraItem?.HopInterval ?? Global.Hysteria2DefaultHopInt; + var protocolExtra = SelectedSource?.GetProtocolExtra(); + Ports = protocolExtra?.Ports ?? string.Empty; + AlterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0; + Flow = protocolExtra?.Flow ?? string.Empty; + UpMbps = protocolExtra?.UpMbps ?? 0; + DownMbps = protocolExtra?.DownMbps ?? 0; + HopInterval = protocolExtra?.HopInterval ?? Global.Hysteria2DefaultHopInt; } private async Task SaveServerAsync() @@ -135,14 +135,14 @@ public class AddServerViewModel : MyReactiveObject SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType); SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert; SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha; - var extraItem = SelectedSource.GetExtraItem(); - extraItem.Ports = Ports; - extraItem.AlterId = AlterId > 0 ? AlterId.ToString() : string.Empty; - extraItem.Flow = Flow; - extraItem.UpMbps = UpMbps; - extraItem.DownMbps = DownMbps; - extraItem.HopInterval = HopInterval >= 5 ? HopInterval : Global.Hysteria2DefaultHopInt; - SelectedSource.SetExtraItem(extraItem); + var protocolExtra = SelectedSource.GetProtocolExtra(); + protocolExtra.Ports = Ports; + protocolExtra.AlterId = AlterId > 0 ? AlterId.ToString() : string.Empty; + protocolExtra.Flow = Flow; + protocolExtra.UpMbps = UpMbps; + protocolExtra.DownMbps = DownMbps; + protocolExtra.HopInterval = HopInterval >= 5 ? HopInterval : Global.Hysteria2DefaultHopInt; + SelectedSource.SetProtocolExtra(protocolExtra); if (await ConfigHandler.AddServer(_config, SelectedSource) == 0) { From ee7d3c60b6d1c1be8f22f04c1570b7b5a2c0c624 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 21 Jan 2026 11:58:29 +0800 Subject: [PATCH 04/20] Refactor id and security --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 94 +++++++++++-------- v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs | 4 +- v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs | 4 +- .../ServiceLib/Handler/Fmt/ShadowsocksFmt.cs | 26 +++-- v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs | 14 ++- v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs | 4 +- v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs | 8 +- v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs | 21 ++--- v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs | 18 ++-- v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs | 34 +++---- .../Manager/ActionPrecheckManager.cs | 18 ++-- v2rayN/ServiceLib/Manager/AppManager.cs | 16 ++++ v2rayN/ServiceLib/Models/ProfileItem.cs | 17 ++-- .../Singbox/SingboxOutboundService.cs | 58 +++++++----- .../CoreConfig/V2ray/V2rayOutboundService.cs | 41 ++++---- .../ViewModels/AddServerViewModel.cs | 55 +++++++++-- .../ViewModels/ProfilesSelectViewModel.cs | 2 +- .../ViewModels/ProfilesViewModel.cs | 2 +- .../Views/AddServerWindow.axaml.cs | 44 ++++----- v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 44 ++++----- 20 files changed, 302 insertions(+), 222 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 2158f88a..6d43b883 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -292,24 +292,28 @@ public static class ConfigHandler { profileItem.ConfigType = EConfigType.VMess; + var protocolExtra = profileItem.GetProtocolExtra(); + profileItem.Address = profileItem.Address.TrimEx(); - profileItem.Id = profileItem.Id.TrimEx(); - profileItem.Security = profileItem.Security.TrimEx(); + profileItem.Password = profileItem.Password.TrimEx(); + protocolExtra.VmessSecurity = protocolExtra.VmessSecurity?.TrimEx(); profileItem.Network = profileItem.Network.TrimEx(); profileItem.HeaderType = profileItem.HeaderType.TrimEx(); profileItem.RequestHost = profileItem.RequestHost.TrimEx(); profileItem.Path = profileItem.Path.TrimEx(); profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx(); - if (!Global.VmessSecurities.Contains(profileItem.Security)) + if (!Global.VmessSecurities.Contains(protocolExtra.VmessSecurity)) { return -1; } - if (profileItem.Id.IsNullOrEmpty()) + if (profileItem.Password.IsNullOrEmpty()) { return -1; } + profileItem.SetProtocolExtra(protocolExtra); + await AddServerCommon(config, profileItem, toFile); return 0; @@ -607,19 +611,23 @@ public static class ConfigHandler { profileItem.ConfigType = EConfigType.Shadowsocks; - profileItem.Address = profileItem.Address.TrimEx(); - profileItem.Id = profileItem.Id.TrimEx(); - profileItem.Security = profileItem.Security.TrimEx(); + var protocolExtra = profileItem.GetProtocolExtra(); - if (!AppManager.Instance.GetShadowsocksSecurities(profileItem).Contains(profileItem.Security)) + profileItem.Address = profileItem.Address.TrimEx(); + profileItem.Password = profileItem.Password.TrimEx(); + protocolExtra.SsMethod = protocolExtra.SsMethod.TrimEx(); + + if (!AppManager.Instance.GetShadowsocksSecurities(profileItem).Contains(protocolExtra.SsMethod)) { return -1; } - if (profileItem.Id.IsNullOrEmpty()) + if (profileItem.Password.IsNullOrEmpty()) { return -1; } + profileItem.SetProtocolExtra(protocolExtra); + await AddServerCommon(config, profileItem, toFile); return 0; @@ -676,12 +684,12 @@ public static class ConfigHandler profileItem.ConfigType = EConfigType.Trojan; profileItem.Address = profileItem.Address.TrimEx(); - profileItem.Id = profileItem.Id.TrimEx(); + profileItem.Password = profileItem.Password.TrimEx(); if (profileItem.StreamSecurity.IsNullOrEmpty()) { profileItem.StreamSecurity = Global.StreamSecurity; } - if (profileItem.Id.IsNullOrEmpty()) + if (profileItem.Password.IsNullOrEmpty()) { return -1; } @@ -706,7 +714,7 @@ public static class ConfigHandler //profileItem.CoreType = ECoreType.sing_box; profileItem.Address = profileItem.Address.TrimEx(); - profileItem.Id = profileItem.Id.TrimEx(); + profileItem.Password = profileItem.Password.TrimEx(); profileItem.Path = profileItem.Path.TrimEx(); profileItem.Network = string.Empty; @@ -714,7 +722,7 @@ public static class ConfigHandler { profileItem.StreamSecurity = Global.StreamSecurity; } - if (profileItem.Id.IsNullOrEmpty()) + if (profileItem.Password.IsNullOrEmpty()) { return -1; } @@ -738,9 +746,11 @@ public static class ConfigHandler profileItem.ConfigType = EConfigType.TUIC; profileItem.CoreType = ECoreType.sing_box; + var protocolExtra = profileItem.GetProtocolExtra(); + profileItem.Address = profileItem.Address.TrimEx(); - profileItem.Id = profileItem.Id.TrimEx(); - profileItem.Security = profileItem.Security.TrimEx(); + profileItem.Password = profileItem.Password.TrimEx(); + protocolExtra.Username = protocolExtra.Username?.TrimEx(); profileItem.Network = string.Empty; if (!Global.TuicCongestionControls.Contains(profileItem.HeaderType)) @@ -756,7 +766,7 @@ public static class ConfigHandler { profileItem.Alpn = "h3"; } - if (profileItem.Id.IsNullOrEmpty()) + if (profileItem.Password.IsNullOrEmpty()) { return -1; } @@ -778,22 +788,26 @@ public static class ConfigHandler { profileItem.ConfigType = EConfigType.WireGuard; + var protocolExtra = profileItem.GetProtocolExtra(); + profileItem.Address = profileItem.Address.TrimEx(); - profileItem.Id = profileItem.Id.TrimEx(); - profileItem.PublicKey = profileItem.PublicKey.TrimEx(); - profileItem.Path = profileItem.Path.TrimEx(); - profileItem.RequestHost = profileItem.RequestHost.TrimEx(); - profileItem.Network = string.Empty; - if (profileItem.ShortId.IsNullOrEmpty()) + profileItem.Password = profileItem.Password.TrimEx(); + protocolExtra.WgPublicKey = protocolExtra.WgPublicKey?.TrimEx(); + protocolExtra.WgPresharedKey = protocolExtra.WgPresharedKey?.TrimEx(); + protocolExtra.WgInterfaceAddress = protocolExtra.WgInterfaceAddress?.TrimEx(); + protocolExtra.WgReserved = protocolExtra.WgReserved?.TrimEx(); + if (protocolExtra.WgMtu <= 0) { - profileItem.ShortId = Global.TunMtus.First().ToString(); + protocolExtra.WgMtu = Global.TunMtus.First(); } - if (profileItem.Id.IsNullOrEmpty()) + if (profileItem.Password.IsNullOrEmpty()) { return -1; } + profileItem.SetProtocolExtra(protocolExtra); + await AddServerCommon(config, profileItem, toFile); return 0; @@ -813,14 +827,13 @@ public static class ConfigHandler profileItem.CoreType = ECoreType.sing_box; profileItem.Address = profileItem.Address.TrimEx(); - profileItem.Id = profileItem.Id.TrimEx(); - profileItem.Security = profileItem.Security.TrimEx(); + profileItem.Password = profileItem.Password.TrimEx(); profileItem.Network = string.Empty; if (profileItem.StreamSecurity.IsNullOrEmpty()) { profileItem.StreamSecurity = Global.StreamSecurity; } - if (profileItem.Id.IsNullOrEmpty()) + if (profileItem.Password.IsNullOrEmpty()) { return -1; } @@ -858,7 +871,7 @@ public static class ConfigHandler Remarks = t.Remarks, Address = t.Address, Port = t.Port, - Security = t.Security, + //Security = t.Security, Network = t.Network, StreamSecurity = t.StreamSecurity, Delay = t33?.Delay ?? 0, @@ -956,28 +969,28 @@ public static class ConfigHandler { profileItem.ConfigType = EConfigType.VLESS; + var protocolExtra = profileItem.GetProtocolExtra(); + profileItem.Address = profileItem.Address.TrimEx(); - profileItem.Id = profileItem.Id.TrimEx(); - profileItem.Security = profileItem.Security.TrimEx(); + profileItem.Password = profileItem.Password.TrimEx(); profileItem.Network = profileItem.Network.TrimEx(); profileItem.HeaderType = profileItem.HeaderType.TrimEx(); profileItem.RequestHost = profileItem.RequestHost.TrimEx(); profileItem.Path = profileItem.Path.TrimEx(); profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx(); - - var protocolExtra = profileItem.GetProtocolExtra(); + protocolExtra.VlessEncryption = protocolExtra.VlessEncryption?.TrimEx(); if (!Global.Flows.Contains(protocolExtra.Flow ?? string.Empty)) { protocolExtra.Flow = Global.Flows.First(); } - if (profileItem.Id.IsNullOrEmpty()) + if (profileItem.Password.IsNullOrEmpty()) { return -1; } - if (profileItem.Security.IsNullOrEmpty()) + if (protocolExtra.VlessEncryption.IsNullOrEmpty()) { - profileItem.Security = Global.None; + protocolExtra.VlessEncryption = Global.None; } profileItem.SetProtocolExtra(protocolExtra); @@ -1099,19 +1112,22 @@ public static class ConfigHandler return false; } - var protocolExtra = o.GetProtocolExtra(); + var oProtocolExtra = o.GetProtocolExtra(); + var nProtocolExtra = n.GetProtocolExtra(); return o.ConfigType == n.ConfigType && AreEqual(o.Address, n.Address) && o.Port == n.Port - && AreEqual(o.Id, n.Id) - && AreEqual(o.Security, n.Security) + && AreEqual(o.Password, n.Password) + && AreEqual(oProtocolExtra.VlessEncryption, nProtocolExtra.VlessEncryption) + && AreEqual(oProtocolExtra.SsMethod, nProtocolExtra.SsMethod) + && AreEqual(oProtocolExtra.VmessSecurity, nProtocolExtra.VmessSecurity) && AreEqual(o.Network, n.Network) && AreEqual(o.HeaderType, n.HeaderType) && AreEqual(o.RequestHost, n.RequestHost) && AreEqual(o.Path, n.Path) && (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity) - && AreEqual(protocolExtra.Flow, protocolExtra.Flow) + && AreEqual(oProtocolExtra.Flow, nProtocolExtra.Flow) && AreEqual(o.Sni, n.Sni) && AreEqual(o.Alpn, n.Alpn) && AreEqual(o.Fingerprint, n.Fingerprint) diff --git a/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs index f098b6a4..d94cbea0 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs @@ -20,7 +20,7 @@ public class AnytlsFmt : BaseFmt Port = parsedUrl.Port, }; var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); - item.Id = rawUserInfo; + item.Password = rawUserInfo; var query = Utils.ParseQueryString(parsedUrl.Query); ResolveUriQuery(query, ref item); @@ -39,7 +39,7 @@ public class AnytlsFmt : BaseFmt { remark = "#" + Utils.UrlEncode(item.Remarks); } - var pw = item.Id; + var pw = item.Password; var dicQuery = new Dictionary(); ToUriQuery(item, Global.None, ref dicQuery); diff --git a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs index 236643d1..759cf3c6 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs @@ -19,7 +19,7 @@ public class Hysteria2Fmt : BaseFmt item.Address = url.IdnHost; item.Port = url.Port; item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - item.Id = Utils.UrlDecode(url.UserInfo); + item.Password = Utils.UrlDecode(url.UserInfo); var query = Utils.ParseQueryString(url.Query); ResolveUriQuery(query, ref item); @@ -75,7 +75,7 @@ public class Hysteria2Fmt : BaseFmt dicQuery.Add("pinSHA256", Utils.UrlEncode(sha)); } - return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark); + return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Password, dicQuery, remark); } public static ProfileItem? ResolveFull2(string strData, string? subRemarks) diff --git a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs index 62f9e120..8838a98d 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs @@ -12,7 +12,9 @@ public class ShadowsocksFmt : BaseFmt { return null; } - if (item.Address.Length == 0 || item.Port == 0 || item.Security.Length == 0 || item.Id.Length == 0) + + var protocolExtra = item.GetProtocolExtra(); + if (item.Address.Length == 0 || item.Port == 0 || protocolExtra.SsMethod.IsNullOrEmpty() || item.Password.Length == 0) { return null; } @@ -40,7 +42,8 @@ public class ShadowsocksFmt : BaseFmt // item.port); //url = Utile.Base64Encode(url); //new Sip002 - var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true); + var protocolExtra = item.GetProtocolExtra(); + var pw = Utils.Base64Encode($"{protocolExtra.SsMethod}:{item.Password}", true); // plugin var plugin = string.Empty; @@ -136,10 +139,12 @@ public class ShadowsocksFmt : BaseFmt { return null; } - item.Security = details.Groups["method"].Value; - item.Id = details.Groups["password"].Value; + var protocolExtra = item.GetProtocolExtra(); + protocolExtra.SsMethod = details.Groups["method"].Value; + item.Password = details.Groups["password"].Value; item.Address = details.Groups["hostname"].Value; item.Port = details.Groups["port"].Value.ToInt(); + item.SetProtocolExtra(protocolExtra); return item; } @@ -157,6 +162,7 @@ public class ShadowsocksFmt : BaseFmt Address = parsedUrl.IdnHost, Port = parsedUrl.Port, }; + var protocolExtra = item.GetProtocolExtra(); var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); //2022-blake3 if (rawUserInfo.Contains(':')) @@ -166,8 +172,8 @@ public class ShadowsocksFmt : BaseFmt { return null; } - item.Security = userInfoParts.First(); - item.Id = Utils.UrlDecode(userInfoParts.Last()); + protocolExtra.SsMethod = userInfoParts.First(); + item.Password = Utils.UrlDecode(userInfoParts.Last()); } else { @@ -178,8 +184,8 @@ public class ShadowsocksFmt : BaseFmt { return null; } - item.Security = userInfoParts.First(); - item.Id = userInfoParts.Last(); + protocolExtra.SsMethod = userInfoParts.First(); + item.Password = userInfoParts.Last(); } var queryParameters = Utils.ParseQueryString(parsedUrl.Query); @@ -300,11 +306,11 @@ public class ShadowsocksFmt : BaseFmt var ssItem = new ProfileItem() { Remarks = it.remarks, - Security = it.method, - Id = it.password, + Password = it.password, Address = it.server, Port = it.server_port.ToInt() }; + ssItem.SetProtocolExtra(new ProtocolExtraItem() { SsMethod = it.method }); lst.Add(ssItem); } return lst; diff --git a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs index ae837793..18e0a68d 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs @@ -33,7 +33,8 @@ public class SocksFmt : BaseFmt remark = "#" + Utils.UrlEncode(item.Remarks); } //new - var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true); + var protocolExtra = item.GetProtocolExtra(); + var pw = Utils.Base64Encode($"{protocolExtra.Username}:{item.Password}", true); return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark); } @@ -43,6 +44,7 @@ public class SocksFmt : BaseFmt { ConfigType = EConfigType.SOCKS }; + var protocolExtra = item.GetProtocolExtra(); result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..]; //remark var indexRemark = result.IndexOf('#'); @@ -78,9 +80,10 @@ public class SocksFmt : BaseFmt } item.Address = arr1[1][..indexPort]; item.Port = arr1[1][(indexPort + 1)..].ToInt(); - item.Security = arr21.First(); - item.Id = arr21[1]; + protocolExtra.Username = arr21.First(); + item.Password = arr21[1]; + item.SetProtocolExtra(protocolExtra); return item; } @@ -98,6 +101,7 @@ public class SocksFmt : BaseFmt Address = parsedUrl.IdnHost, Port = parsedUrl.Port, }; + var protocolExtra = item.GetProtocolExtra(); // parse base64 UserInfo var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); @@ -105,8 +109,8 @@ public class SocksFmt : BaseFmt var userInfoParts = userInfo.Split([':'], 2); if (userInfoParts.Length == 2) { - item.Security = userInfoParts.First(); - item.Id = userInfoParts[1]; + protocolExtra.Username = userInfoParts.First(); + item.Password = userInfoParts[1]; } return item; diff --git a/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs index dc4794d8..c58d6802 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs @@ -20,7 +20,7 @@ public class TrojanFmt : BaseFmt item.Address = url.IdnHost; item.Port = url.Port; item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - item.Id = Utils.UrlDecode(url.UserInfo); + item.Password = Utils.UrlDecode(url.UserInfo); var query = Utils.ParseQueryString(url.Query); ResolveUriQuery(query, ref item); @@ -42,6 +42,6 @@ public class TrojanFmt : BaseFmt var dicQuery = new Dictionary(); ToUriQuery(item, null, ref dicQuery); - return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark); + return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Password, dicQuery, remark); } } diff --git a/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs index 1c5aded6..3f8be224 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs @@ -10,6 +10,7 @@ public class TuicFmt : BaseFmt { ConfigType = EConfigType.TUIC }; + var protocolExtra = item.GetProtocolExtra(); var url = Utils.TryUri(str); if (url == null) @@ -24,14 +25,15 @@ public class TuicFmt : BaseFmt var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); if (userInfoParts.Length == 2) { - item.Id = userInfoParts.First(); - item.Security = userInfoParts.Last(); + item.Password = userInfoParts.First(); + protocolExtra.Username = userInfoParts.Last(); } var query = Utils.ParseQueryString(url.Query); ResolveUriQuery(query, ref item); item.HeaderType = GetQueryValue(query, "congestion_control"); + item.SetProtocolExtra(protocolExtra); return item; } @@ -53,6 +55,6 @@ public class TuicFmt : BaseFmt dicQuery.Add("congestion_control", item.HeaderType); - return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark); + return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Password}:{item.GetProtocolExtra().Username ?? ""}", dicQuery, remark); } } diff --git a/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs index 3048b51c..da6c5bd5 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs @@ -9,8 +9,8 @@ public class VLESSFmt : BaseFmt ProfileItem item = new() { ConfigType = EConfigType.VLESS, - Security = Global.None }; + var protocolExtra = item.GetProtocolExtra(); var url = Utils.TryUri(str); if (url == null) @@ -21,13 +21,14 @@ public class VLESSFmt : BaseFmt item.Address = url.IdnHost; item.Port = url.Port; item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - item.Id = Utils.UrlDecode(url.UserInfo); + item.Password = Utils.UrlDecode(url.UserInfo); var query = Utils.ParseQueryString(url.Query); - item.Security = GetQueryValue(query, "encryption", Global.None); + protocolExtra.VlessEncryption = GetQueryValue(query, "encryption", Global.None); item.StreamSecurity = GetQueryValue(query, "security"); ResolveUriQuery(query, ref item); + item.SetProtocolExtra(protocolExtra); return item; } @@ -38,22 +39,18 @@ public class VLESSFmt : BaseFmt return null; } + var protocolExtra = item.GetProtocolExtra(); + var remark = string.Empty; if (item.Remarks.IsNotEmpty()) { remark = "#" + Utils.UrlEncode(item.Remarks); } var dicQuery = new Dictionary(); - if (item.Security.IsNotEmpty()) - { - dicQuery.Add("encryption", item.Security); - } - else - { - dicQuery.Add("encryption", Global.None); - } + dicQuery.Add("encryption", + !protocolExtra.VlessEncryption.IsNullOrEmpty() ? protocolExtra.VlessEncryption : Global.None); ToUriQuery(item, Global.None, ref dicQuery); - return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark); + return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Password, dicQuery, remark); } } diff --git a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs index 52438508..58874b6d 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs @@ -31,9 +31,9 @@ public class VmessFmt : BaseFmt ps = item.Remarks.TrimEx(), add = item.Address, port = item.Port, - id = item.Id, + id = item.Password, aid = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0, - scy = item.Security, + scy = item.GetProtocolExtra().VmessSecurity ?? "", net = item.Network, type = item.HeaderType, host = item.RequestHost, @@ -77,14 +77,12 @@ public class VmessFmt : BaseFmt item.Remarks = Utils.ToString(vmessQRCode.ps); item.Address = Utils.ToString(vmessQRCode.add); item.Port = vmessQRCode.port; - item.Id = Utils.ToString(vmessQRCode.id); + item.Password = Utils.ToString(vmessQRCode.id); item.SetProtocolExtra(new ProtocolExtraItem { AlterId = vmessQRCode.aid.ToString(), + VmessSecurity = vmessQRCode.scy.IsNullOrEmpty() ? Global.DefaultSecurity : vmessQRCode.scy, }); - item.Security = Utils.ToString(vmessQRCode.scy); - - item.Security = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity; if (vmessQRCode.net.IsNotEmpty()) { item.Network = vmessQRCode.net; @@ -110,7 +108,6 @@ public class VmessFmt : BaseFmt var item = new ProfileItem { ConfigType = EConfigType.VMess, - Security = "auto" }; var url = Utils.TryUri(str); @@ -122,7 +119,12 @@ public class VmessFmt : BaseFmt item.Address = url.IdnHost; item.Port = url.Port; item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - item.Id = Utils.UrlDecode(url.UserInfo); + item.Password = Utils.UrlDecode(url.UserInfo); + + item.SetProtocolExtra(new ProtocolExtraItem + { + VmessSecurity = "auto", + }); var query = Utils.ParseQueryString(url.Query); ResolveUriQuery(query, ref item); diff --git a/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs index 6ceb945d..c003a96b 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs @@ -17,17 +17,20 @@ public class WireguardFmt : BaseFmt return null; } + var protocolExtra = item.GetProtocolExtra(); + item.Address = url.IdnHost; item.Port = url.Port; item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - item.Id = Utils.UrlDecode(url.UserInfo); + item.Password = Utils.UrlDecode(url.UserInfo); var query = Utils.ParseQueryString(url.Query); - item.PublicKey = GetQueryDecoded(query, "publickey"); - item.Path = GetQueryDecoded(query, "reserved"); - item.RequestHost = GetQueryDecoded(query, "address"); - item.ShortId = GetQueryDecoded(query, "mtu"); + protocolExtra.WgPublicKey = GetQueryDecoded(query, "publickey"); + protocolExtra.WgReserved = GetQueryDecoded(query, "reserved"); + protocolExtra.WgInterfaceAddress = GetQueryDecoded(query, "address"); + protocolExtra.WgMtu = int.TryParse(GetQueryDecoded(query, "mtu"), out var mtu) ? mtu : 1280; + protocolExtra.WgPresharedKey = GetQueryDecoded(query, "presharedKey"); return item; } @@ -39,6 +42,8 @@ public class WireguardFmt : BaseFmt return null; } + var protocolExtra = item.GetProtocolExtra(); + var remark = string.Empty; if (item.Remarks.IsNotEmpty()) { @@ -46,22 +51,19 @@ public class WireguardFmt : BaseFmt } var dicQuery = new Dictionary(); - if (item.PublicKey.IsNotEmpty()) + if (!protocolExtra.WgPublicKey.IsNullOrEmpty()) { - dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey)); + dicQuery.Add("publickey", Utils.UrlEncode(protocolExtra.WgPublicKey)); } - if (item.Path.IsNotEmpty()) + if (!protocolExtra.WgReserved.IsNullOrEmpty()) { - dicQuery.Add("reserved", Utils.UrlEncode(item.Path)); + dicQuery.Add("reserved", Utils.UrlEncode(protocolExtra.WgReserved)); } - if (item.RequestHost.IsNotEmpty()) + if (!protocolExtra.WgInterfaceAddress.IsNullOrEmpty()) { - dicQuery.Add("address", Utils.UrlEncode(item.RequestHost)); + dicQuery.Add("address", Utils.UrlEncode(protocolExtra.WgInterfaceAddress)); } - if (item.ShortId.IsNotEmpty()) - { - dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId)); - } - return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark); + dicQuery.Add("mtu", Utils.UrlEncode(protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu.ToString() : "1280")); + return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Password, dicQuery, remark); } } diff --git a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs index 78c78ba0..b5718bf5 100644 --- a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs +++ b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs @@ -1,3 +1,5 @@ +using ServiceLib.Common; + namespace ServiceLib.Manager; /// @@ -133,17 +135,17 @@ public class ActionPrecheckManager switch (item.ConfigType) { case EConfigType.VMess: - if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id)) + if (item.Password.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Password)) { - errors.Add(string.Format(ResUI.InvalidProperty, "Id")); + errors.Add(string.Format(ResUI.InvalidProperty, "Password")); } break; case EConfigType.VLESS: - if (item.Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Id) && item.Id.Length > 30)) + if (item.Password.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Password) && item.Password.Length > 30)) { - errors.Add(string.Format(ResUI.InvalidProperty, "Id")); + errors.Add(string.Format(ResUI.InvalidProperty, "Password")); } if (!Global.Flows.Contains(protocolExtra.Flow ?? string.Empty)) @@ -154,14 +156,14 @@ public class ActionPrecheckManager break; case EConfigType.Shadowsocks: - if (item.Id.IsNullOrEmpty()) + if (item.Password.IsNullOrEmpty()) { - errors.Add(string.Format(ResUI.InvalidProperty, "Id")); + errors.Add(string.Format(ResUI.InvalidProperty, "Password")); } - if (string.IsNullOrEmpty(item.Security) || !Global.SsSecuritiesInSingbox.Contains(item.Security)) + if (string.IsNullOrEmpty(protocolExtra.SsMethod) || !Global.SsSecuritiesInSingbox.Contains(protocolExtra.SsMethod)) { - errors.Add(string.Format(ResUI.InvalidProperty, "Security")); + errors.Add(string.Format(ResUI.InvalidProperty, "SsMethod")); } break; diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 2acf9246..1951cce0 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -294,6 +294,22 @@ public sealed class AppManager extra.Filter = groupItem.Filter; extra.MultipleLoad = groupItem.MultipleLoad; } + + switch (item.ConfigType) + { + case EConfigType.Shadowsocks: + extra.SsMethod = item.Security.IsNotEmpty() ? item.Security : null; + break; + case EConfigType.VMess: + extra.VmessSecurity = item.Security.IsNotEmpty() ? item.Security : null; + break; + case EConfigType.WireGuard: + extra.WgPublicKey = item.PublicKey.IsNotEmpty() ? item.PublicKey : null; + extra.WgInterfaceAddress = item.RequestHost.IsNotEmpty() ? item.RequestHost : null; + extra.WgReserved = item.Path.IsNotEmpty() ? item.Path : null; + extra.WgMtu = int.TryParse(item.ShortId, out var mtu) ? mtu : 1280; + break; + } } item.SetProtocolExtra(extra); diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs index 478754bc..4142de93 100644 --- a/v2rayN/ServiceLib/Models/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileItem.cs @@ -1,3 +1,5 @@ +using ServiceLib.Common; + namespace ServiceLib.Models; [Serializable] @@ -11,9 +13,6 @@ public class ProfileItem : ReactiveObject Address = string.Empty; Port = 0; Password = string.Empty; - Id = string.Empty; - AlterId = 0; - Security = string.Empty; Network = string.Empty; Remarks = string.Empty; HeaderType = string.Empty; @@ -22,7 +21,6 @@ public class ProfileItem : ReactiveObject StreamSecurity = string.Empty; AllowInsecure = string.Empty; Subid = string.Empty; - Flow = string.Empty; } #region function @@ -82,7 +80,7 @@ public class ProfileItem : ReactiveObject switch (ConfigType) { case EConfigType.VMess: - if (Id.IsNullOrEmpty() || !Utils.IsGuidByParse(Id)) + if (Password.IsNullOrEmpty() || !Utils.IsGuidByParse(Password)) { return false; } @@ -90,7 +88,7 @@ public class ProfileItem : ReactiveObject break; case EConfigType.VLESS: - if (Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(Id) && Id.Length > 30)) + if (Password.IsNullOrEmpty() || (!Utils.IsGuidByParse(Password) && Password.Length > 30)) { return false; } @@ -103,12 +101,15 @@ public class ProfileItem : ReactiveObject break; case EConfigType.Shadowsocks: - if (Id.IsNullOrEmpty()) + if (Password.IsNullOrEmpty()) { return false; } - if (string.IsNullOrEmpty(Security) || !Global.SsSecuritiesInSingbox.Contains(Security)) + var protocolExtra = GetProtocolExtra(); + + if (string.IsNullOrEmpty(protocolExtra.SsMethod) + || !Global.SsSecuritiesInSingbox.Contains(protocolExtra.SsMethod)) { return false; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index edc17a16..fbbb5191 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -1,3 +1,5 @@ +using System.Linq; + namespace ServiceLib.Services.CoreConfig; public partial class CoreConfigSingboxService @@ -15,11 +17,11 @@ public partial class CoreConfigSingboxService { case EConfigType.VMess: { - outbound.uuid = node.Id; - outbound.alter_id = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0; - if (Global.VmessSecurities.Contains(node.Security)) + outbound.uuid = node.Password; + outbound.alter_id = int.TryParse(protocolExtra.AlterId, out var result) ? result : 0; + if (Global.VmessSecurities.Contains(protocolExtra.VmessSecurity)) { - outbound.security = node.Security; + outbound.security = protocolExtra.VmessSecurity; } else { @@ -32,8 +34,9 @@ public partial class CoreConfigSingboxService } case EConfigType.Shadowsocks: { - outbound.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : Global.None; - outbound.password = node.Id; + outbound.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(protocolExtra.SsMethod) + ? protocolExtra.SsMethod : Global.None; + outbound.password = node.Password; if (node.Network == nameof(ETransport.tcp) && node.HeaderType == Global.TcpHeaderHttp) { @@ -89,27 +92,27 @@ public partial class CoreConfigSingboxService case EConfigType.SOCKS: { outbound.version = "5"; - if (node.Security.IsNotEmpty() - && node.Id.IsNotEmpty()) + if (protocolExtra.Username.IsNotEmpty() + && node.Password.IsNotEmpty()) { - outbound.username = node.Security; - outbound.password = node.Id; + outbound.username = protocolExtra.Username; + outbound.password = node.Password; } break; } case EConfigType.HTTP: { - if (node.Security.IsNotEmpty() - && node.Id.IsNotEmpty()) + if (protocolExtra.Username.IsNotEmpty() + && node.Password.IsNotEmpty()) { - outbound.username = node.Security; - outbound.password = node.Id; + outbound.username = protocolExtra.Username; + outbound.password = node.Password; } break; } case EConfigType.VLESS: { - outbound.uuid = node.Id; + outbound.uuid = node.Password; outbound.packet_encoding = "xudp"; @@ -127,7 +130,7 @@ public partial class CoreConfigSingboxService } case EConfigType.Trojan: { - outbound.password = node.Id; + outbound.password = node.Password; await GenOutboundMux(node, outbound); await GenOutboundTransport(node, outbound); @@ -135,7 +138,7 @@ public partial class CoreConfigSingboxService } case EConfigType.Hysteria2: { - outbound.password = node.Id; + outbound.password = node.Password; if (node.Path.IsNotEmpty()) { @@ -174,14 +177,14 @@ public partial class CoreConfigSingboxService } case EConfigType.TUIC: { - outbound.uuid = node.Id; - outbound.password = node.Security; + outbound.uuid = node.Password; + outbound.password = protocolExtra.Username; outbound.congestion_control = node.HeaderType; break; } case EConfigType.Anytls: { - outbound.password = node.Id; + outbound.password = node.Password; break; } } @@ -199,7 +202,9 @@ public partial class CoreConfigSingboxService { try { - endpoint.address = Utils.String2List(node.RequestHost); + var protocolExtra = node.GetProtocolExtra(); + + endpoint.address = Utils.String2List(protocolExtra.WgInterfaceAddress); endpoint.type = Global.ProtocolTypes[node.ConfigType]; switch (node.ConfigType) @@ -208,16 +213,17 @@ public partial class CoreConfigSingboxService { var peer = new Peer4Sbox { - public_key = node.PublicKey, - reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(), + public_key = protocolExtra.WgPublicKey, + pre_shared_key = protocolExtra.WgPresharedKey, + reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(int.Parse).ToList(), address = node.Address, port = node.Port, // TODO default ["0.0.0.0/0", "::/0"] allowed_ips = new() { "0.0.0.0/0", "::/0" }, }; - endpoint.private_key = node.Id; - endpoint.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(); - endpoint.peers = new() { peer }; + endpoint.private_key = node.Password; + endpoint.mtu = protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu : Global.TunMtus.First(); + endpoint.peers = [peer]; break; } } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index 7ff29768..8dfb4cc0 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -1,3 +1,5 @@ +using System.Linq; + namespace ServiceLib.Services.CoreConfig; public partial class CoreConfigV2rayService @@ -36,12 +38,12 @@ public partial class CoreConfigV2rayService usersItem = vnextItem.users.First(); } - usersItem.id = node.Id; + usersItem.id = node.Password; usersItem.alterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0; usersItem.email = Global.UserEMail; - if (Global.VmessSecurities.Contains(node.Security)) + if (Global.VmessSecurities.Contains(protocolExtra.VmessSecurity)) { - usersItem.security = node.Security; + usersItem.security = protocolExtra.VmessSecurity; } else { @@ -67,8 +69,9 @@ public partial class CoreConfigV2rayService } serversItem.address = node.Address; serversItem.port = node.Port; - serversItem.password = node.Id; - serversItem.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : "none"; + serversItem.password = node.Password; + serversItem.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(protocolExtra.SsMethod) + ? protocolExtra.SsMethod : "none"; serversItem.ota = false; serversItem.level = 1; @@ -96,13 +99,13 @@ public partial class CoreConfigV2rayService serversItem.method = null; serversItem.password = null; - if (node.Security.IsNotEmpty() - && node.Id.IsNotEmpty()) + if (protocolExtra.Username.IsNotEmpty() + && node.Password.IsNotEmpty()) { SocksUsersItem4Ray socksUsersItem = new() { - user = node.Security, - pass = node.Id, + user = protocolExtra.Username ?? "", + pass = node.Password, level = 1 }; @@ -139,9 +142,9 @@ public partial class CoreConfigV2rayService { usersItem = vnextItem.users.First(); } - usersItem.id = node.Id; + usersItem.id = node.Password; usersItem.email = Global.UserEMail; - usersItem.encryption = node.Security; + usersItem.encryption = protocolExtra.VlessEncryption; if (protocolExtra.Flow.IsNullOrEmpty()) { @@ -169,7 +172,7 @@ public partial class CoreConfigV2rayService } serversItem.address = node.Address; serversItem.port = node.Port; - serversItem.password = node.Id; + serversItem.password = node.Password; serversItem.ota = false; serversItem.level = 1; @@ -200,16 +203,16 @@ public partial class CoreConfigV2rayService } var peer = new WireguardPeer4Ray { - publicKey = node.PublicKey, + publicKey = protocolExtra.WgPublicKey ?? "", endpoint = address + ":" + node.Port.ToString() }; var setting = new Outboundsettings4Ray { - address = Utils.String2List(node.RequestHost), - secretKey = node.Id, - reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(), - mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(), - peers = new List { peer } + address = Utils.String2List(protocolExtra.WgInterfaceAddress), + secretKey = node.Password, + reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(int.Parse).ToList(), + mtu = protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu : Global.TunMtus.First(), + peers = [peer] }; outbound.settings = setting; outbound.settings.vnext = null; @@ -534,7 +537,7 @@ public partial class CoreConfigV2rayService HysteriaSettings4Ray hysteriaSettings = new() { version = 2, - auth = node.Id, + auth = node.Password, up = upMbps > 0 ? $"{upMbps}mbps" : null, down = downMbps > 0 ? $"{downMbps}mbps" : null, udphop = udpHop, diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index 99a52c12..7c1b3895 100644 --- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -35,6 +35,29 @@ public class AddServerViewModel : MyReactiveObject [Reactive] public string Flow { get; set; } + [Reactive] + public string VmessSecurity { get; set; } + + [Reactive] + public string VlessEncryption { get; set; } + + [Reactive] + public string SsMethod { get; set; } + + [Reactive] + public string Username { get; set; } + + [Reactive] + public string WgPublicKey { get; set; } + //[Reactive] + //public string WgPresharedKey { get; set; } + [Reactive] + public string WgInterfaceAddress { get; set; } + [Reactive] + public string WgReserved { get; set; } + [Reactive] + public int WgMtu { get; set; } + public ReactiveCommand FetchCertCmd { get; } public ReactiveCommand FetchCertChainCmd { get; } public ReactiveCommand SaveCmd { get; } @@ -89,6 +112,14 @@ public class AddServerViewModel : MyReactiveObject UpMbps = protocolExtra?.UpMbps ?? 0; DownMbps = protocolExtra?.DownMbps ?? 0; HopInterval = protocolExtra?.HopInterval ?? Global.Hysteria2DefaultHopInt; + VmessSecurity = protocolExtra?.VmessSecurity?.IsNullOrEmpty() == false ? protocolExtra.VmessSecurity : Global.DefaultSecurity; + VlessEncryption = protocolExtra?.VlessEncryption.IsNullOrEmpty() == false ? protocolExtra.VlessEncryption : Global.None; + SsMethod = protocolExtra?.SsMethod ?? string.Empty; + Username = protocolExtra?.Username ?? string.Empty; + WgPublicKey = protocolExtra?.WgPublicKey ?? string.Empty; + WgInterfaceAddress = protocolExtra?.WgInterfaceAddress ?? string.Empty; + WgReserved = protocolExtra?.WgReserved ?? string.Empty; + WgMtu = protocolExtra?.WgMtu ?? 1280; } private async Task SaveServerAsync() @@ -113,12 +144,12 @@ public class AddServerViewModel : MyReactiveObject } if (SelectedSource.ConfigType == EConfigType.Shadowsocks) { - if (SelectedSource.Id.IsNullOrEmpty()) + if (SelectedSource.Password.IsNullOrEmpty()) { NoticeManager.Instance.Enqueue(ResUI.FillPassword); return; } - if (SelectedSource.Security.IsNullOrEmpty()) + if (SsMethod.IsNullOrEmpty()) { NoticeManager.Instance.Enqueue(ResUI.PleaseSelectEncryption); return; @@ -126,7 +157,7 @@ public class AddServerViewModel : MyReactiveObject } if (SelectedSource.ConfigType is not EConfigType.SOCKS and not EConfigType.HTTP) { - if (SelectedSource.Id.IsNullOrEmpty()) + if (SelectedSource.Password.IsNullOrEmpty()) { NoticeManager.Instance.Enqueue(ResUI.FillUUID); return; @@ -136,12 +167,20 @@ public class AddServerViewModel : MyReactiveObject SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert; SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha; var protocolExtra = SelectedSource.GetProtocolExtra(); - protocolExtra.Ports = Ports; + protocolExtra.Ports = Ports.IsNullOrEmpty() ? null : Ports; protocolExtra.AlterId = AlterId > 0 ? AlterId.ToString() : string.Empty; - protocolExtra.Flow = Flow; - protocolExtra.UpMbps = UpMbps; - protocolExtra.DownMbps = DownMbps; - protocolExtra.HopInterval = HopInterval >= 5 ? HopInterval : Global.Hysteria2DefaultHopInt; + protocolExtra.Flow = Flow.IsNullOrEmpty() ? null : Flow; + protocolExtra.UpMbps = UpMbps > 0 ? UpMbps : null; + protocolExtra.DownMbps = DownMbps > 0 ? DownMbps : null; + protocolExtra.HopInterval = HopInterval >= 5 ? HopInterval : null; + protocolExtra.VmessSecurity = VmessSecurity.IsNullOrEmpty() ? null : VmessSecurity; + protocolExtra.VlessEncryption = VlessEncryption.IsNullOrEmpty() ? null : VlessEncryption; + protocolExtra.SsMethod = SsMethod.IsNullOrEmpty() ? null : SsMethod; + protocolExtra.Username = Username.IsNullOrEmpty() ? null : Username; + protocolExtra.WgPublicKey = WgPublicKey.IsNullOrEmpty() ? null : WgPublicKey; + protocolExtra.WgInterfaceAddress = WgInterfaceAddress.IsNullOrEmpty() ? null : WgInterfaceAddress; + protocolExtra.WgReserved = WgReserved.IsNullOrEmpty() ? null : WgReserved; + protocolExtra.WgMtu = WgMtu >= 576 ? WgMtu : null; SelectedSource.SetProtocolExtra(protocolExtra); if (await ConfigHandler.AddServer(_config, SelectedSource) == 0) diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesSelectViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesSelectViewModel.cs index 7301882c..ca065d5d 100644 --- a/v2rayN/ServiceLib/ViewModels/ProfilesSelectViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ProfilesSelectViewModel.cs @@ -209,7 +209,7 @@ public class ProfilesSelectViewModel : MyReactiveObject Remarks = t.Remarks, Address = t.Address, Port = t.Port, - Security = t.Security, + //Security = t.Security, Network = t.Network, StreamSecurity = t.StreamSecurity, Subid = t.Subid, diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs index 0e141b74..7bd4cb98 100644 --- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs @@ -446,7 +446,7 @@ public class ProfilesViewModel : MyReactiveObject Remarks = t.Remarks, Address = t.Address, Port = t.Port, - Security = t.Security, + //Security = t.Security, Network = t.Network, StreamSecurity = t.StreamSecurity, Subid = t.Subid, diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index a8696f15..c8e86ca9 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -39,10 +39,6 @@ public partial class AddServerWindow : WindowBase case EConfigType.VMess: gridVMess.IsVisible = true; cmbSecurity.ItemsSource = Global.VmessSecurities; - if (profileItem.Security.IsNullOrEmpty()) - { - profileItem.Security = Global.DefaultSecurity; - } break; case EConfigType.Shadowsocks: @@ -59,10 +55,6 @@ public partial class AddServerWindow : WindowBase gridVLESS.IsVisible = true; lstStreamSecurity.Add(Global.StreamSecurityReality); cmbFlow5.ItemsSource = Global.Flows; - if (profileItem.Security.IsNullOrEmpty()) - { - profileItem.Security = Global.None; - } break; case EConfigType.Trojan: @@ -119,39 +111,39 @@ public partial class AddServerWindow : WindowBase switch (profileItem.ConfigType) { case EConfigType.VMess: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.VmessSecurity, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables); break; case EConfigType.Shadowsocks: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId3.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables); break; case EConfigType.SOCKS: case EConfigType.HTTP: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId4.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity4.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Username, v => v.txtSecurity4.Text).DisposeWith(disposables); break; case EConfigType.VLESS: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId5.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.VlessEncryption, v => v.txtSecurity5.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables); break; case EConfigType.Trojan: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId6.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables); break; case EConfigType.Hysteria2: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables); @@ -160,21 +152,21 @@ public partial class AddServerWindow : WindowBase break; case EConfigType.TUIC: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId8.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity8.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId8.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Username, v => v.txtSecurity8.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables); break; case EConfigType.WireGuard: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId9.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath9.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(disposables); break; case EConfigType.Anytls: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables); break; } this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index e939d74f..fa105a8e 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -34,10 +34,6 @@ public partial class AddServerWindow case EConfigType.VMess: gridVMess.Visibility = Visibility.Visible; cmbSecurity.ItemsSource = Global.VmessSecurities; - if (profileItem.Security.IsNullOrEmpty()) - { - profileItem.Security = Global.DefaultSecurity; - } break; case EConfigType.Shadowsocks: @@ -54,10 +50,6 @@ public partial class AddServerWindow gridVLESS.Visibility = Visibility.Visible; lstStreamSecurity.Add(Global.StreamSecurityReality); cmbFlow5.ItemsSource = Global.Flows; - if (profileItem.Security.IsNullOrEmpty()) - { - profileItem.Security = Global.None; - } break; case EConfigType.Trojan: @@ -114,39 +106,39 @@ public partial class AddServerWindow switch (profileItem.ConfigType) { case EConfigType.VMess: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.VmessSecurity, v => v.cmbSecurity.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables); break; case EConfigType.Shadowsocks: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId3.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity3.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables); break; case EConfigType.SOCKS: case EConfigType.HTTP: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId4.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity4.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Username, v => v.txtSecurity4.Text).DisposeWith(disposables); break; case EConfigType.VLESS: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId5.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.VlessEncryption, v => v.txtSecurity5.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables); break; case EConfigType.Trojan: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId6.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables); break; case EConfigType.Hysteria2: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables); @@ -155,21 +147,21 @@ public partial class AddServerWindow break; case EConfigType.TUIC: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId8.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity8.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId8.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Username, v => v.txtSecurity8.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.Text).DisposeWith(disposables); break; case EConfigType.WireGuard: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId9.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath9.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(disposables); break; case EConfigType.Anytls: - this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables); break; } this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables); From 82443a64cda5e63af677ce9e4115cc1d61a980df Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 21 Jan 2026 12:03:23 +0800 Subject: [PATCH 05/20] Refactor flow --- v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs | 9 --------- v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs | 8 ++++++++ v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs | 5 +++++ v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs | 2 -- v2rayN/ServiceLib/Models/ProfileItem.cs | 2 -- .../CoreConfig/Singbox/SingboxOutboundService.cs | 2 -- .../Services/CoreConfig/V2ray/V2rayOutboundService.cs | 2 -- 7 files changed, 13 insertions(+), 17 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs index 1ff712b7..bfafce13 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs @@ -21,12 +21,6 @@ public class BaseFmt protected static int ToUriQuery(ProfileItem item, string? securityDef, ref Dictionary dicQuery) { - var protocolExtra = item.GetProtocolExtra(); - if (!protocolExtra.Flow.IsNullOrEmpty()) - { - dicQuery.Add("flow", protocolExtra.Flow); - } - if (item.StreamSecurity.IsNotEmpty()) { dicQuery.Add("security", item.StreamSecurity); @@ -209,9 +203,6 @@ public class BaseFmt protected static int ResolveUriQuery(NameValueCollection query, ref ProfileItem item) { - var protocolExtra = item.GetProtocolExtra(); - protocolExtra.Flow = GetQueryValue(query, "flow"); - item.SetProtocolExtra(protocolExtra); item.StreamSecurity = GetQueryValue(query, "security"); item.Sni = GetQueryValue(query, "sni"); item.Alpn = GetQueryDecoded(query, "alpn"); diff --git a/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs index c58d6802..5180a883 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs @@ -10,6 +10,7 @@ public class TrojanFmt : BaseFmt { ConfigType = EConfigType.Trojan }; + var protocolExtra = item.GetProtocolExtra(); var url = Utils.TryUri(str); if (url == null) @@ -23,8 +24,10 @@ public class TrojanFmt : BaseFmt item.Password = Utils.UrlDecode(url.UserInfo); var query = Utils.ParseQueryString(url.Query); + protocolExtra.Flow = GetQueryValue(query, "flow"); ResolveUriQuery(query, ref item); + item.SetProtocolExtra(protocolExtra); return item; } @@ -34,12 +37,17 @@ public class TrojanFmt : BaseFmt { return null; } + var protocolExtra = item.GetProtocolExtra(); var remark = string.Empty; if (item.Remarks.IsNotEmpty()) { remark = "#" + Utils.UrlEncode(item.Remarks); } var dicQuery = new Dictionary(); + if (!protocolExtra.Flow.IsNullOrEmpty()) + { + dicQuery.Add("flow", protocolExtra.Flow); + } ToUriQuery(item, null, ref dicQuery); return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Password, dicQuery, remark); diff --git a/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs index da6c5bd5..a48c3c05 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs @@ -25,6 +25,7 @@ public class VLESSFmt : BaseFmt var query = Utils.ParseQueryString(url.Query); protocolExtra.VlessEncryption = GetQueryValue(query, "encryption", Global.None); + protocolExtra.Flow = GetQueryValue(query, "flow"); item.StreamSecurity = GetQueryValue(query, "security"); ResolveUriQuery(query, ref item); @@ -49,6 +50,10 @@ public class VLESSFmt : BaseFmt var dicQuery = new Dictionary(); dicQuery.Add("encryption", !protocolExtra.VlessEncryption.IsNullOrEmpty() ? protocolExtra.VlessEncryption : Global.None); + if (!protocolExtra.Flow.IsNullOrEmpty()) + { + dicQuery.Add("flow", protocolExtra.Flow); + } ToUriQuery(item, Global.None, ref dicQuery); return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Password, dicQuery, remark); diff --git a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs index b5718bf5..ee3cd5a0 100644 --- a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs +++ b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs @@ -1,5 +1,3 @@ -using ServiceLib.Common; - namespace ServiceLib.Manager; /// diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs index 4142de93..847eab60 100644 --- a/v2rayN/ServiceLib/Models/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileItem.cs @@ -1,5 +1,3 @@ -using ServiceLib.Common; - namespace ServiceLib.Models; [Serializable] diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index fbbb5191..ed49cb66 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -1,5 +1,3 @@ -using System.Linq; - namespace ServiceLib.Services.CoreConfig; public partial class CoreConfigSingboxService diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index 8dfb4cc0..d5d18b3a 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -1,5 +1,3 @@ -using System.Linq; - namespace ServiceLib.Services.CoreConfig; public partial class CoreConfigV2rayService From 7bfd2f504da6657908ff3546c026fb74f51ca9b6 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 21 Jan 2026 12:11:06 +0800 Subject: [PATCH 06/20] Fix hy2 bbr --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 16 ++++++++++++++++ .../CoreConfig/Singbox/SingboxOutboundService.cs | 4 ++-- .../CoreConfig/V2ray/V2rayOutboundService.cs | 4 ++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 6d43b883..80cd9354 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -713,6 +713,8 @@ public static class ConfigHandler profileItem.ConfigType = EConfigType.Hysteria2; //profileItem.CoreType = ECoreType.sing_box; + var protocolExtra = profileItem.GetProtocolExtra(); + profileItem.Address = profileItem.Address.TrimEx(); profileItem.Password = profileItem.Password.TrimEx(); profileItem.Path = profileItem.Path.TrimEx(); @@ -726,6 +728,20 @@ public static class ConfigHandler { return -1; } + if (protocolExtra.UpMbps is null or < 0) + { + protocolExtra.UpMbps = config.HysteriaItem.UpMbps; + } + if (protocolExtra.DownMbps is null or < 0) + { + protocolExtra.DownMbps = config.HysteriaItem.DownMbps; + } + if (protocolExtra.HopInterval is null or <= 5) + { + protocolExtra.HopInterval = Global.Hysteria2DefaultHopInt; + } + + profileItem.SetProtocolExtra(protocolExtra); await AddServerCommon(config, profileItem, toFile); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index ed49cb66..4467425c 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -149,10 +149,10 @@ public partial class CoreConfigSingboxService outbound.up_mbps = protocolExtra?.UpMbps is { } su and >= 0 ? su - : _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null; + : 0; outbound.down_mbps = protocolExtra?.DownMbps is { } sd and >= 0 ? sd - : _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null; + : 0; var ports = protocolExtra?.Ports?.IsNullOrEmpty() == false ? protocolExtra.Ports : null; if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index d5d18b3a..b67df735 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -515,10 +515,10 @@ public partial class CoreConfigV2rayService var ports = protocolExtra?.Ports; int? upMbps = protocolExtra?.UpMbps is { } su and >= 0 ? su - : _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null; + : 0; int? downMbps = protocolExtra?.DownMbps is { } sd and >= 0 ? sd - : _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null; + : 0; var hopInterval = protocolExtra?.HopInterval is { } hi and >= 5 ? hi : _config.HysteriaItem.HopInterval >= 5 ? _config.HysteriaItem.HopInterval : Global.Hysteria2DefaultHopInt; From 189ab47fa9e2df511c2eb0e0c04de86fb0d86370 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 21 Jan 2026 12:15:21 +0800 Subject: [PATCH 07/20] Fix warning CS0618 --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 80cd9354..d010acba 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -230,12 +230,6 @@ public static class ConfigHandler item.Remarks = profileItem.Remarks; item.Address = profileItem.Address; item.Port = profileItem.Port; - item.Ports = profileItem.Ports; - - item.Id = profileItem.Id; - item.AlterId = profileItem.AlterId; - item.Security = profileItem.Security; - item.Flow = profileItem.Flow; item.Password = profileItem.Password; From 0eae23ace0957043f8328c14731fff0b8d910f0e Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 21 Jan 2026 12:21:47 +0800 Subject: [PATCH 08/20] Remove unused code --- v2rayN/ServiceLib/Manager/AppManager.cs | 7 ++- .../Manager/ProfileGroupItemManager.cs | 46 ------------------- .../ViewModels/MainWindowViewModel.cs | 1 - 3 files changed, 5 insertions(+), 49 deletions(-) delete mode 100644 v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 1951cce0..e4d53c3e 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -262,8 +262,11 @@ public sealed class AppManager public async Task MigrateProfileExtra() { #pragma warning disable CS0618 + var list = await SQLiteHelper.Instance.TableAsync().ToListAsync(); + var groupItems = new ConcurrentDictionary(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!)); + const int pageSize = 500; - int offset = 0; + var offset = 0; while (true) { @@ -286,7 +289,7 @@ public sealed class AppManager if (item.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain) { extra.GroupType = nameof(item.ConfigType); - ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var groupItem); + groupItems.TryGetValue(item.IndexId, out var groupItem); if (groupItem != null && !groupItem.NotHasChild()) { extra.ChildItems = groupItem.ChildItems; diff --git a/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs b/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs deleted file mode 100644 index 97b037d1..00000000 --- a/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace ServiceLib.Manager; - -[Obsolete("Use GroupProfileManager instead.")] -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); - } - - private async Task InitData() - { - await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem where IndexId not in ( select indexId from ProfileItem )"); - - var list = await SQLiteHelper.Instance.TableAsync().ToListAsync(); - _items = new ConcurrentDictionary(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!)); - } - - public async Task ClearAll() - { - await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem "); - _items.Clear(); - } -} diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index a09ad4de..8f5e9029 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -259,7 +259,6 @@ public class MainWindowViewModel : MyReactiveObject await ConfigHandler.InitBuiltinDNS(_config); await ConfigHandler.InitBuiltinFullConfigTemplate(_config); await ProfileExManager.Instance.Init(); - await ProfileGroupItemManager.Instance.Init(); await CoreManager.Instance.Init(_config, UpdateHandler); TaskManager.Instance.RegUpdateTask(_config, UpdateTaskHandler); From 2c454aee953473c362e9809cba043aa44ad8d5e5 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 21 Jan 2026 12:23:40 +0800 Subject: [PATCH 09/20] Fix hy2 migrate --- v2rayN/ServiceLib/Manager/AppManager.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index e4d53c3e..3838626e 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -306,6 +306,11 @@ public sealed class AppManager case EConfigType.VMess: extra.VmessSecurity = item.Security.IsNotEmpty() ? item.Security : null; break; + case EConfigType.Hysteria2: + extra.UpMbps = _config.HysteriaItem.UpMbps; + extra.DownMbps = _config.HysteriaItem.DownMbps; + extra.HopInterval = _config.HysteriaItem.HopInterval; + break; case EConfigType.WireGuard: extra.WgPublicKey = item.PublicKey.IsNotEmpty() ? item.PublicKey : null; extra.WgInterfaceAddress = item.RequestHost.IsNotEmpty() ? item.RequestHost : null; From 274fc8084dbe1b5989d2b190a940efda0f89c00f Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 21 Jan 2026 15:53:59 +0800 Subject: [PATCH 10/20] Fix --- v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs | 1 + v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs index 8838a98d..c613a296 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs @@ -282,6 +282,7 @@ public class ShadowsocksFmt : BaseFmt } } + item.SetProtocolExtra(protocolExtra); return item; } diff --git a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs index 18e0a68d..35f10bd3 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs @@ -113,6 +113,7 @@ public class SocksFmt : BaseFmt item.Password = userInfoParts[1]; } + item.SetProtocolExtra(protocolExtra); return item; } } From 323bf7047474057c3fde3b852990be5319533ea3 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 21 Jan 2026 19:48:41 +0800 Subject: [PATCH 11/20] Refactor --- v2rayN/ServiceLib/Models/ProfileItem.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs index 847eab60..5e060b0b 100644 --- a/v2rayN/ServiceLib/Models/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileItem.cs @@ -3,6 +3,8 @@ namespace ServiceLib.Models; [Serializable] public class ProfileItem : ReactiveObject { + private ProtocolExtraItem _protocolExtraItem = new(); + public ProfileItem() { IndexId = string.Empty; @@ -127,16 +129,12 @@ public class ProfileItem : ReactiveObject public void SetProtocolExtra(ProtocolExtraItem extraItem) { - ProtoExtra = JsonUtils.Serialize(extraItem, false); + _protocolExtraItem = extraItem; } public ProtocolExtraItem GetProtocolExtra() { - if (ProtoExtra.IsNullOrEmpty()) - { - return new ProtocolExtraItem(); - } - return JsonUtils.Deserialize(ProtoExtra); + return _protocolExtraItem; } #endregion function @@ -174,7 +172,13 @@ public class ProfileItem : ReactiveObject public string CertSha { get; set; } public string EchConfigList { get; set; } public string EchForceQuery { get; set; } - public string ProtoExtra { get; set; } + + public string ProtoExtra + { + get => JsonUtils.Serialize(_protocolExtraItem, false); + set => _protocolExtraItem = JsonUtils.Deserialize(value); + } + [Obsolete("Use ProtocolExtraItem.Ports instead.")] public string Ports { get; set; } [Obsolete("Use ProtocolExtraItem.AlterId instead.")] From 3b826e3e368676a168948e6e7e61237a8c230f7c Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 21 Jan 2026 20:59:10 +0800 Subject: [PATCH 12/20] Refactor ProfileItem protocol extra handling --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 1 + v2rayN/ServiceLib/Models/ProfileItem.cs | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index d010acba..d2cda968 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -1102,6 +1102,7 @@ public static class ConfigHandler if (toFile) { + profileItem.SetProtocolExtra(); await SQLiteHelper.Instance.ReplaceAsync(profileItem); } return 0; diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs index 5e060b0b..60cbe9b2 100644 --- a/v2rayN/ServiceLib/Models/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileItem.cs @@ -3,7 +3,7 @@ namespace ServiceLib.Models; [Serializable] public class ProfileItem : ReactiveObject { - private ProtocolExtraItem _protocolExtraItem = new(); + private ProtocolExtraItem? _protocolExtraCache; public ProfileItem() { @@ -129,12 +129,18 @@ public class ProfileItem : ReactiveObject public void SetProtocolExtra(ProtocolExtraItem extraItem) { - _protocolExtraItem = extraItem; + _protocolExtraCache = extraItem; + ProtoExtra = JsonUtils.Serialize(extraItem, false); + } + + public void SetProtocolExtra() + { + ProtoExtra = JsonUtils.Serialize(_protocolExtraCache, false); } public ProtocolExtraItem GetProtocolExtra() { - return _protocolExtraItem; + return _protocolExtraCache ??= JsonUtils.Deserialize(ProtoExtra) ?? new ProtocolExtraItem(); } #endregion function @@ -173,20 +179,20 @@ public class ProfileItem : ReactiveObject public string EchConfigList { get; set; } public string EchForceQuery { get; set; } - public string ProtoExtra - { - get => JsonUtils.Serialize(_protocolExtraItem, false); - set => _protocolExtraItem = JsonUtils.Deserialize(value); - } + public string ProtoExtra { get; set; } [Obsolete("Use ProtocolExtraItem.Ports instead.")] public string Ports { get; set; } + [Obsolete("Use ProtocolExtraItem.AlterId instead.")] public int AlterId { get; set; } + [Obsolete("Use ProtocolExtraItem.Flow instead.")] public string Flow { get; set; } + [Obsolete("Use ProfileItem.Password instead.")] public string Id { get; set; } + [Obsolete("Use ProtocolExtraItem.xxx instead.")] public string Security { get; set; } } From 37734d2de90ba16a8e6f707cb448f3b568f58e17 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 21 Jan 2026 21:04:00 +0800 Subject: [PATCH 13/20] Refactor, use record instead of class --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 86 +++++++------------ v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs | 5 +- .../ServiceLib/Handler/Fmt/ShadowsocksFmt.cs | 17 ++-- v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs | 13 +-- v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs | 9 +- v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs | 4 +- v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs | 17 ++-- v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs | 3 +- v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs | 30 +++---- v2rayN/ServiceLib/Manager/AppManager.cs | 37 +++++--- v2rayN/ServiceLib/Models/ProfileItem.cs | 6 +- v2rayN/ServiceLib/Models/ProtocolExtraItem.cs | 46 +++++----- .../ViewModels/AddGroupServerViewModel.cs | 33 ++++--- .../ViewModels/AddServerViewModel.cs | 33 +++---- 14 files changed, 151 insertions(+), 188 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index d2cda968..a8b4bf13 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -286,18 +286,19 @@ public static class ConfigHandler { profileItem.ConfigType = EConfigType.VMess; - var protocolExtra = profileItem.GetProtocolExtra(); - profileItem.Address = profileItem.Address.TrimEx(); profileItem.Password = profileItem.Password.TrimEx(); - protocolExtra.VmessSecurity = protocolExtra.VmessSecurity?.TrimEx(); + profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with + { + VmessSecurity = profileItem.GetProtocolExtra().VmessSecurity?.TrimEx() + }); profileItem.Network = profileItem.Network.TrimEx(); profileItem.HeaderType = profileItem.HeaderType.TrimEx(); profileItem.RequestHost = profileItem.RequestHost.TrimEx(); profileItem.Path = profileItem.Path.TrimEx(); profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx(); - if (!Global.VmessSecurities.Contains(protocolExtra.VmessSecurity)) + if (!Global.VmessSecurities.Contains(profileItem.GetProtocolExtra().VmessSecurity)) { return -1; } @@ -306,8 +307,6 @@ public static class ConfigHandler return -1; } - profileItem.SetProtocolExtra(protocolExtra); - await AddServerCommon(config, profileItem, toFile); return 0; @@ -605,13 +604,14 @@ public static class ConfigHandler { profileItem.ConfigType = EConfigType.Shadowsocks; - var protocolExtra = profileItem.GetProtocolExtra(); - profileItem.Address = profileItem.Address.TrimEx(); profileItem.Password = profileItem.Password.TrimEx(); - protocolExtra.SsMethod = protocolExtra.SsMethod.TrimEx(); + profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with + { + SsMethod = profileItem.GetProtocolExtra().SsMethod?.TrimEx() + }); - if (!AppManager.Instance.GetShadowsocksSecurities(profileItem).Contains(protocolExtra.SsMethod)) + if (!AppManager.Instance.GetShadowsocksSecurities(profileItem).Contains(profileItem.GetProtocolExtra().SsMethod)) { return -1; } @@ -620,8 +620,6 @@ public static class ConfigHandler return -1; } - profileItem.SetProtocolExtra(protocolExtra); - await AddServerCommon(config, profileItem, toFile); return 0; @@ -707,8 +705,6 @@ public static class ConfigHandler profileItem.ConfigType = EConfigType.Hysteria2; //profileItem.CoreType = ECoreType.sing_box; - var protocolExtra = profileItem.GetProtocolExtra(); - profileItem.Address = profileItem.Address.TrimEx(); profileItem.Password = profileItem.Password.TrimEx(); profileItem.Path = profileItem.Path.TrimEx(); @@ -722,20 +718,12 @@ public static class ConfigHandler { return -1; } - if (protocolExtra.UpMbps is null or < 0) + profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with { - protocolExtra.UpMbps = config.HysteriaItem.UpMbps; - } - if (protocolExtra.DownMbps is null or < 0) - { - protocolExtra.DownMbps = config.HysteriaItem.DownMbps; - } - if (protocolExtra.HopInterval is null or <= 5) - { - protocolExtra.HopInterval = Global.Hysteria2DefaultHopInt; - } - - profileItem.SetProtocolExtra(protocolExtra); + UpMbps = profileItem.GetProtocolExtra().UpMbps is null or < 0 ? config.HysteriaItem.UpMbps : profileItem.GetProtocolExtra().UpMbps, + DownMbps = profileItem.GetProtocolExtra().DownMbps is null or < 0 ? config.HysteriaItem.DownMbps : profileItem.GetProtocolExtra().DownMbps, + HopInterval = profileItem.GetProtocolExtra().HopInterval is null or <= 5 ? Global.Hysteria2DefaultHopInt : profileItem.GetProtocolExtra().HopInterval, + }); await AddServerCommon(config, profileItem, toFile); @@ -756,11 +744,12 @@ public static class ConfigHandler profileItem.ConfigType = EConfigType.TUIC; profileItem.CoreType = ECoreType.sing_box; - var protocolExtra = profileItem.GetProtocolExtra(); - profileItem.Address = profileItem.Address.TrimEx(); profileItem.Password = profileItem.Password.TrimEx(); - protocolExtra.Username = protocolExtra.Username?.TrimEx(); + profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with + { + Username = profileItem.GetProtocolExtra().Username?.TrimEx() + }); profileItem.Network = string.Empty; if (!Global.TuicCongestionControls.Contains(profileItem.HeaderType)) @@ -798,26 +787,22 @@ public static class ConfigHandler { profileItem.ConfigType = EConfigType.WireGuard; - var protocolExtra = profileItem.GetProtocolExtra(); - profileItem.Address = profileItem.Address.TrimEx(); profileItem.Password = profileItem.Password.TrimEx(); - protocolExtra.WgPublicKey = protocolExtra.WgPublicKey?.TrimEx(); - protocolExtra.WgPresharedKey = protocolExtra.WgPresharedKey?.TrimEx(); - protocolExtra.WgInterfaceAddress = protocolExtra.WgInterfaceAddress?.TrimEx(); - protocolExtra.WgReserved = protocolExtra.WgReserved?.TrimEx(); - if (protocolExtra.WgMtu <= 0) + profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with { - protocolExtra.WgMtu = Global.TunMtus.First(); - } + WgPublicKey = profileItem.GetProtocolExtra().WgPublicKey?.TrimEx(), + WgPresharedKey = profileItem.GetProtocolExtra().WgPresharedKey?.TrimEx(), + WgInterfaceAddress = profileItem.GetProtocolExtra().WgInterfaceAddress?.TrimEx(), + WgReserved = profileItem.GetProtocolExtra().WgReserved?.TrimEx(), + WgMtu = profileItem.GetProtocolExtra().WgMtu is null or <= 0 ? Global.TunMtus.First() : profileItem.GetProtocolExtra().WgMtu, + }); if (profileItem.Password.IsNullOrEmpty()) { return -1; } - profileItem.SetProtocolExtra(protocolExtra); - await AddServerCommon(config, profileItem, toFile); return 0; @@ -979,8 +964,6 @@ public static class ConfigHandler { profileItem.ConfigType = EConfigType.VLESS; - var protocolExtra = profileItem.GetProtocolExtra(); - profileItem.Address = profileItem.Address.TrimEx(); profileItem.Password = profileItem.Password.TrimEx(); profileItem.Network = profileItem.Network.TrimEx(); @@ -988,22 +971,19 @@ public static class ConfigHandler profileItem.RequestHost = profileItem.RequestHost.TrimEx(); profileItem.Path = profileItem.Path.TrimEx(); profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx(); - protocolExtra.VlessEncryption = protocolExtra.VlessEncryption?.TrimEx(); - if (!Global.Flows.Contains(protocolExtra.Flow ?? string.Empty)) + var vlessEncryption = profileItem.GetProtocolExtra().VlessEncryption?.TrimEx(); + var flow = profileItem.GetProtocolExtra().Flow?.TrimEx() ?? string.Empty; + profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with { - protocolExtra.Flow = Global.Flows.First(); - } + VlessEncryption = vlessEncryption.IsNullOrEmpty() ? Global.None : vlessEncryption, + Flow = Global.Flows.Contains(flow) ? flow : Global.Flows.First(), + }); + if (profileItem.Password.IsNullOrEmpty()) { return -1; } - if (protocolExtra.VlessEncryption.IsNullOrEmpty()) - { - protocolExtra.VlessEncryption = Global.None; - } - - profileItem.SetProtocolExtra(protocolExtra); await AddServerCommon(config, profileItem, toFile); diff --git a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs index 759cf3c6..59833e5d 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs @@ -59,10 +59,9 @@ public class Hysteria2Fmt : BaseFmt dicQuery.Add("obfs", "salamander"); dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path)); } - var protocolExtra = item.GetProtocolExtra(); - if (protocolExtra?.Ports?.IsNotEmpty() ?? false) + if (item.GetProtocolExtra()?.Ports?.IsNotEmpty() ?? false) { - dicQuery.Add("mport", Utils.UrlEncode(protocolExtra.Ports.Replace(':', '-'))); + dicQuery.Add("mport", Utils.UrlEncode(item.GetProtocolExtra().Ports.Replace(':', '-'))); } if (!item.CertSha.IsNullOrEmpty()) { diff --git a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs index c613a296..5b30fa46 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs @@ -13,8 +13,7 @@ public class ShadowsocksFmt : BaseFmt return null; } - var protocolExtra = item.GetProtocolExtra(); - if (item.Address.Length == 0 || item.Port == 0 || protocolExtra.SsMethod.IsNullOrEmpty() || item.Password.Length == 0) + if (item.Address.Length == 0 || item.Port == 0 || item.GetProtocolExtra().SsMethod.IsNullOrEmpty() || item.Password.Length == 0) { return null; } @@ -42,8 +41,7 @@ public class ShadowsocksFmt : BaseFmt // item.port); //url = Utile.Base64Encode(url); //new Sip002 - var protocolExtra = item.GetProtocolExtra(); - var pw = Utils.Base64Encode($"{protocolExtra.SsMethod}:{item.Password}", true); + var pw = Utils.Base64Encode($"{item.GetProtocolExtra().SsMethod}:{item.Password}", true); // plugin var plugin = string.Empty; @@ -139,12 +137,10 @@ public class ShadowsocksFmt : BaseFmt { return null; } - var protocolExtra = item.GetProtocolExtra(); - protocolExtra.SsMethod = details.Groups["method"].Value; + item.SetProtocolExtra(item.GetProtocolExtra() with { SsMethod = details.Groups["method"].Value }); item.Password = details.Groups["password"].Value; item.Address = details.Groups["hostname"].Value; item.Port = details.Groups["port"].Value.ToInt(); - item.SetProtocolExtra(protocolExtra); return item; } @@ -162,7 +158,6 @@ public class ShadowsocksFmt : BaseFmt Address = parsedUrl.IdnHost, Port = parsedUrl.Port, }; - var protocolExtra = item.GetProtocolExtra(); var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); //2022-blake3 if (rawUserInfo.Contains(':')) @@ -172,7 +167,7 @@ public class ShadowsocksFmt : BaseFmt { return null; } - protocolExtra.SsMethod = userInfoParts.First(); + item.SetProtocolExtra(item.GetProtocolExtra() with { SsMethod = userInfoParts.First() }); item.Password = Utils.UrlDecode(userInfoParts.Last()); } else @@ -184,7 +179,7 @@ public class ShadowsocksFmt : BaseFmt { return null; } - protocolExtra.SsMethod = userInfoParts.First(); + item.SetProtocolExtra(item.GetProtocolExtra() with { SsMethod = userInfoParts.First() }); item.Password = userInfoParts.Last(); } @@ -281,8 +276,6 @@ public class ShadowsocksFmt : BaseFmt } } } - - item.SetProtocolExtra(protocolExtra); return item; } diff --git a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs index 35f10bd3..be996aba 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs @@ -33,8 +33,7 @@ public class SocksFmt : BaseFmt remark = "#" + Utils.UrlEncode(item.Remarks); } //new - var protocolExtra = item.GetProtocolExtra(); - var pw = Utils.Base64Encode($"{protocolExtra.Username}:{item.Password}", true); + var pw = Utils.Base64Encode($"{item.GetProtocolExtra().Username}:{item.Password}", true); return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark); } @@ -44,7 +43,6 @@ public class SocksFmt : BaseFmt { ConfigType = EConfigType.SOCKS }; - var protocolExtra = item.GetProtocolExtra(); result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..]; //remark var indexRemark = result.IndexOf('#'); @@ -80,10 +78,8 @@ public class SocksFmt : BaseFmt } item.Address = arr1[1][..indexPort]; item.Port = arr1[1][(indexPort + 1)..].ToInt(); - protocolExtra.Username = arr21.First(); + item.SetProtocolExtra(item.GetProtocolExtra() with { Username = arr21.First() }); item.Password = arr21[1]; - - item.SetProtocolExtra(protocolExtra); return item; } @@ -101,19 +97,16 @@ public class SocksFmt : BaseFmt Address = parsedUrl.IdnHost, Port = parsedUrl.Port, }; - var protocolExtra = item.GetProtocolExtra(); - // parse base64 UserInfo var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); var userInfo = Utils.Base64Decode(rawUserInfo); var userInfoParts = userInfo.Split([':'], 2); if (userInfoParts.Length == 2) { - protocolExtra.Username = userInfoParts.First(); + item.SetProtocolExtra(item.GetProtocolExtra() with { Username = userInfoParts.First() }); item.Password = userInfoParts[1]; } - item.SetProtocolExtra(protocolExtra); return item; } } diff --git a/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs index 5180a883..630dead1 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs @@ -10,7 +10,6 @@ public class TrojanFmt : BaseFmt { ConfigType = EConfigType.Trojan }; - var protocolExtra = item.GetProtocolExtra(); var url = Utils.TryUri(str); if (url == null) @@ -24,10 +23,9 @@ public class TrojanFmt : BaseFmt item.Password = Utils.UrlDecode(url.UserInfo); var query = Utils.ParseQueryString(url.Query); - protocolExtra.Flow = GetQueryValue(query, "flow"); + item.SetProtocolExtra(item.GetProtocolExtra() with { Flow = GetQueryValue(query, "flow") }); ResolveUriQuery(query, ref item); - item.SetProtocolExtra(protocolExtra); return item; } @@ -37,16 +35,15 @@ public class TrojanFmt : BaseFmt { return null; } - var protocolExtra = item.GetProtocolExtra(); var remark = string.Empty; if (item.Remarks.IsNotEmpty()) { remark = "#" + Utils.UrlEncode(item.Remarks); } var dicQuery = new Dictionary(); - if (!protocolExtra.Flow.IsNullOrEmpty()) + if (!item.GetProtocolExtra().Flow.IsNullOrEmpty()) { - dicQuery.Add("flow", protocolExtra.Flow); + dicQuery.Add("flow", item.GetProtocolExtra().Flow); } ToUriQuery(item, null, ref dicQuery); diff --git a/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs index 3f8be224..b17b1ef2 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs @@ -10,7 +10,6 @@ public class TuicFmt : BaseFmt { ConfigType = EConfigType.TUIC }; - var protocolExtra = item.GetProtocolExtra(); var url = Utils.TryUri(str); if (url == null) @@ -26,14 +25,13 @@ public class TuicFmt : BaseFmt if (userInfoParts.Length == 2) { item.Password = userInfoParts.First(); - protocolExtra.Username = userInfoParts.Last(); + item.SetProtocolExtra(item.GetProtocolExtra() with { Username = userInfoParts.Last() }); } var query = Utils.ParseQueryString(url.Query); ResolveUriQuery(query, ref item); item.HeaderType = GetQueryValue(query, "congestion_control"); - item.SetProtocolExtra(protocolExtra); return item; } diff --git a/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs index a48c3c05..a3463bcd 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs @@ -10,7 +10,6 @@ public class VLESSFmt : BaseFmt { ConfigType = EConfigType.VLESS, }; - var protocolExtra = item.GetProtocolExtra(); var url = Utils.TryUri(str); if (url == null) @@ -24,12 +23,14 @@ public class VLESSFmt : BaseFmt item.Password = Utils.UrlDecode(url.UserInfo); var query = Utils.ParseQueryString(url.Query); - protocolExtra.VlessEncryption = GetQueryValue(query, "encryption", Global.None); - protocolExtra.Flow = GetQueryValue(query, "flow"); + item.SetProtocolExtra(item.GetProtocolExtra() with + { + VlessEncryption = GetQueryValue(query, "encryption", Global.None), + Flow = GetQueryValue(query, "flow") + }); item.StreamSecurity = GetQueryValue(query, "security"); ResolveUriQuery(query, ref item); - item.SetProtocolExtra(protocolExtra); return item; } @@ -40,8 +41,6 @@ public class VLESSFmt : BaseFmt return null; } - var protocolExtra = item.GetProtocolExtra(); - var remark = string.Empty; if (item.Remarks.IsNotEmpty()) { @@ -49,10 +48,10 @@ public class VLESSFmt : BaseFmt } var dicQuery = new Dictionary(); dicQuery.Add("encryption", - !protocolExtra.VlessEncryption.IsNullOrEmpty() ? protocolExtra.VlessEncryption : Global.None); - if (!protocolExtra.Flow.IsNullOrEmpty()) + !item.GetProtocolExtra().VlessEncryption.IsNullOrEmpty() ? item.GetProtocolExtra().VlessEncryption : Global.None); + if (!item.GetProtocolExtra().Flow.IsNullOrEmpty()) { - dicQuery.Add("flow", protocolExtra.Flow); + dicQuery.Add("flow", item.GetProtocolExtra().Flow); } ToUriQuery(item, Global.None, ref dicQuery); diff --git a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs index 58874b6d..b8760a40 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs @@ -24,7 +24,6 @@ public class VmessFmt : BaseFmt return null; } - var protocolExtra = item?.GetProtocolExtra(); var vmessQRCode = new VmessQRCode { v = 2, @@ -32,7 +31,7 @@ public class VmessFmt : BaseFmt add = item.Address, port = item.Port, id = item.Password, - aid = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0, + aid = int.TryParse(item.GetProtocolExtra()?.AlterId, out var result) ? result : 0, scy = item.GetProtocolExtra().VmessSecurity ?? "", net = item.Network, type = item.HeaderType, diff --git a/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs index c003a96b..73cb6a85 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs @@ -17,8 +17,6 @@ public class WireguardFmt : BaseFmt return null; } - var protocolExtra = item.GetProtocolExtra(); - item.Address = url.IdnHost; item.Port = url.Port; item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); @@ -26,11 +24,13 @@ public class WireguardFmt : BaseFmt var query = Utils.ParseQueryString(url.Query); - protocolExtra.WgPublicKey = GetQueryDecoded(query, "publickey"); - protocolExtra.WgReserved = GetQueryDecoded(query, "reserved"); - protocolExtra.WgInterfaceAddress = GetQueryDecoded(query, "address"); - protocolExtra.WgMtu = int.TryParse(GetQueryDecoded(query, "mtu"), out var mtu) ? mtu : 1280; - protocolExtra.WgPresharedKey = GetQueryDecoded(query, "presharedKey"); + item.SetProtocolExtra(item.GetProtocolExtra() with + { + WgPublicKey = GetQueryDecoded(query, "publickey"), + WgReserved = GetQueryDecoded(query, "reserved"), + WgInterfaceAddress = GetQueryDecoded(query, "address"), + WgMtu = int.TryParse(GetQueryDecoded(query, "mtu"), out var mtuVal) ? mtuVal : 1280, + }); return item; } @@ -42,8 +42,6 @@ public class WireguardFmt : BaseFmt return null; } - var protocolExtra = item.GetProtocolExtra(); - var remark = string.Empty; if (item.Remarks.IsNotEmpty()) { @@ -51,19 +49,19 @@ public class WireguardFmt : BaseFmt } var dicQuery = new Dictionary(); - if (!protocolExtra.WgPublicKey.IsNullOrEmpty()) + if (!item.GetProtocolExtra().WgPublicKey.IsNullOrEmpty()) { - dicQuery.Add("publickey", Utils.UrlEncode(protocolExtra.WgPublicKey)); + dicQuery.Add("publickey", Utils.UrlEncode(item.GetProtocolExtra().WgPublicKey)); } - if (!protocolExtra.WgReserved.IsNullOrEmpty()) + if (!item.GetProtocolExtra().WgReserved.IsNullOrEmpty()) { - dicQuery.Add("reserved", Utils.UrlEncode(protocolExtra.WgReserved)); + dicQuery.Add("reserved", Utils.UrlEncode(item.GetProtocolExtra().WgReserved)); } - if (!protocolExtra.WgInterfaceAddress.IsNullOrEmpty()) + if (!item.GetProtocolExtra().WgInterfaceAddress.IsNullOrEmpty()) { - dicQuery.Add("address", Utils.UrlEncode(protocolExtra.WgInterfaceAddress)); + dicQuery.Add("address", Utils.UrlEncode(item.GetProtocolExtra().WgInterfaceAddress)); } - dicQuery.Add("mtu", Utils.UrlEncode(protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu.ToString() : "1280")); + dicQuery.Add("mtu", Utils.UrlEncode(item.GetProtocolExtra().WgMtu > 0 ? item.GetProtocolExtra().WgMtu.ToString() : "1280")); return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Password, dicQuery, remark); } } diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 3838626e..7753cac8 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -288,34 +288,43 @@ public sealed class AppManager if (item.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain) { - extra.GroupType = nameof(item.ConfigType); + extra = extra with { GroupType = nameof(item.ConfigType) }; groupItems.TryGetValue(item.IndexId, out var groupItem); if (groupItem != null && !groupItem.NotHasChild()) { - extra.ChildItems = groupItem.ChildItems; - extra.SubChildItems = groupItem.SubChildItems; - extra.Filter = groupItem.Filter; - extra.MultipleLoad = groupItem.MultipleLoad; + extra = extra with + { + ChildItems = groupItem.ChildItems, + SubChildItems = groupItem.SubChildItems, + Filter = groupItem.Filter, + MultipleLoad = groupItem.MultipleLoad, + }; } switch (item.ConfigType) { case EConfigType.Shadowsocks: - extra.SsMethod = item.Security.IsNotEmpty() ? item.Security : null; + extra = extra with {SsMethod = item.Security.IsNotEmpty() ? item.Security : null}; break; case EConfigType.VMess: - extra.VmessSecurity = item.Security.IsNotEmpty() ? item.Security : null; + extra = extra with {VmessSecurity = item.Security.IsNotEmpty() ? item.Security : null}; break; case EConfigType.Hysteria2: - extra.UpMbps = _config.HysteriaItem.UpMbps; - extra.DownMbps = _config.HysteriaItem.DownMbps; - extra.HopInterval = _config.HysteriaItem.HopInterval; + extra = extra with + { + UpMbps = _config.HysteriaItem.UpMbps, + DownMbps = _config.HysteriaItem.DownMbps, + HopInterval = _config.HysteriaItem.HopInterval + }; break; case EConfigType.WireGuard: - extra.WgPublicKey = item.PublicKey.IsNotEmpty() ? item.PublicKey : null; - extra.WgInterfaceAddress = item.RequestHost.IsNotEmpty() ? item.RequestHost : null; - extra.WgReserved = item.Path.IsNotEmpty() ? item.Path : null; - extra.WgMtu = int.TryParse(item.ShortId, out var mtu) ? mtu : 1280; + extra = extra with + { + WgPublicKey = item.PublicKey.IsNotEmpty() ? item.PublicKey : null, + WgInterfaceAddress = item.RequestHost.IsNotEmpty() ? item.RequestHost : null, + WgReserved = item.Path.IsNotEmpty() ? item.Path : null, + WgMtu = int.TryParse(item.ShortId, out var mtu) ? mtu : 1280 + }; break; } } diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs index 60cbe9b2..f8be1cfe 100644 --- a/v2rayN/ServiceLib/Models/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileItem.cs @@ -106,10 +106,8 @@ public class ProfileItem : ReactiveObject return false; } - var protocolExtra = GetProtocolExtra(); - - if (string.IsNullOrEmpty(protocolExtra.SsMethod) - || !Global.SsSecuritiesInSingbox.Contains(protocolExtra.SsMethod)) + if (string.IsNullOrEmpty(GetProtocolExtra().SsMethod) + || !Global.SsSecuritiesInSingbox.Contains(GetProtocolExtra().SsMethod)) { return false; } diff --git a/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs index eea4698c..0233b542 100644 --- a/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs +++ b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs @@ -1,40 +1,40 @@ namespace ServiceLib.Models; -public class ProtocolExtraItem +public record ProtocolExtraItem { // vmess - public string? AlterId { get; set; } - public string? VmessSecurity { get; set; } + public string? AlterId { get; init; } + public string? VmessSecurity { get; init; } // vless - public string? Flow { get; set; } - public string? VlessEncryption { get; set; } - //public string? VisionSeed { get; set; } + public string? Flow { get; init; } + public string? VlessEncryption { get; init; } + //public string? VisionSeed { get; init; } // shadowsocks - //public string? PluginArgs { get; set; } - public string? SsMethod { get; set; } + //public string? PluginArgs { get; init; } + public string? SsMethod { get; init; } // socks and http - public string? Username { get; set; } + public string? Username { get; init; } // wireguard - public string? WgPublicKey { get; set; } - public string? WgPresharedKey { get; set; } - public string? WgInterfaceAddress { get; set; } - public string? WgReserved { get; set; } - public int? WgMtu { get; set; } + public string? WgPublicKey { get; init; } + public string? WgPresharedKey { get; init; } + public string? WgInterfaceAddress { get; init; } + public string? WgReserved { get; init; } + public int? WgMtu { get; init; } // hysteria2 - public int? UpMbps { get; set; } - public int? DownMbps { get; set; } - public string? Ports { get; set; } - public int? HopInterval { get; set; } + public int? UpMbps { get; init; } + public int? DownMbps { get; init; } + public string? Ports { get; init; } + public int? HopInterval { get; init; } // group profile - public string? GroupType { get; set; } - public string? ChildItems { get; set; } - public string? SubChildItems { get; set; } - public string? Filter { get; set; } - public EMultipleLoad? MultipleLoad { get; set; } + public string? GroupType { get; init; } + public string? ChildItems { get; init; } + public string? SubChildItems { get; init; } + public string? Filter { get; init; } + public EMultipleLoad? MultipleLoad { get; init; } } diff --git a/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs index 0bedeac5..c19d218b 100644 --- a/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs @@ -202,31 +202,30 @@ public class AddGroupServerViewModel : MyReactiveObject return; } - var protocolExtra = SelectedSource.GetProtocolExtra(); - protocolExtra.ChildItems = - Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList()); - protocolExtra.MultipleLoad = PolicyGroupType switch + SelectedSource.SetProtocolExtra(SelectedSource.GetProtocolExtra() with { - var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing, - var s when s == ResUI.TbFallback => EMultipleLoad.Fallback, - var s when s == ResUI.TbRandom => EMultipleLoad.Random, - var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin, - var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad, - _ => EMultipleLoad.LeastPing, - }; + ChildItems = + Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList()), + MultipleLoad = PolicyGroupType switch + { + var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing, + var s when s == ResUI.TbFallback => EMultipleLoad.Fallback, + var s when s == ResUI.TbRandom => EMultipleLoad.Random, + var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin, + var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad, + _ => EMultipleLoad.LeastPing, + }, + SubChildItems = SelectedSubItem?.Id, + Filter = Filter, + }); - protocolExtra.SubChildItems = SelectedSubItem?.Id; - protocolExtra.Filter = Filter; - - var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, protocolExtra); + var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, SelectedSource.GetProtocolExtra()); if (hasCycle) { NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks)); return; } - SelectedSource.SetProtocolExtra(protocolExtra); - if (await ConfigHandler.AddServerCommon(_config, SelectedSource) == 0) { NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index 7c1b3895..fd204969 100644 --- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -166,22 +166,23 @@ public class AddServerViewModel : MyReactiveObject SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType); SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert; SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha; - var protocolExtra = SelectedSource.GetProtocolExtra(); - protocolExtra.Ports = Ports.IsNullOrEmpty() ? null : Ports; - protocolExtra.AlterId = AlterId > 0 ? AlterId.ToString() : string.Empty; - protocolExtra.Flow = Flow.IsNullOrEmpty() ? null : Flow; - protocolExtra.UpMbps = UpMbps > 0 ? UpMbps : null; - protocolExtra.DownMbps = DownMbps > 0 ? DownMbps : null; - protocolExtra.HopInterval = HopInterval >= 5 ? HopInterval : null; - protocolExtra.VmessSecurity = VmessSecurity.IsNullOrEmpty() ? null : VmessSecurity; - protocolExtra.VlessEncryption = VlessEncryption.IsNullOrEmpty() ? null : VlessEncryption; - protocolExtra.SsMethod = SsMethod.IsNullOrEmpty() ? null : SsMethod; - protocolExtra.Username = Username.IsNullOrEmpty() ? null : Username; - protocolExtra.WgPublicKey = WgPublicKey.IsNullOrEmpty() ? null : WgPublicKey; - protocolExtra.WgInterfaceAddress = WgInterfaceAddress.IsNullOrEmpty() ? null : WgInterfaceAddress; - protocolExtra.WgReserved = WgReserved.IsNullOrEmpty() ? null : WgReserved; - protocolExtra.WgMtu = WgMtu >= 576 ? WgMtu : null; - SelectedSource.SetProtocolExtra(protocolExtra); + SelectedSource.SetProtocolExtra(SelectedSource.GetProtocolExtra() with + { + Ports = Ports.IsNullOrEmpty() ? null : Ports, + AlterId = AlterId > 0 ? AlterId.ToString() : string.Empty, + Flow = Flow.IsNullOrEmpty() ? null : Flow, + UpMbps = UpMbps > 0 ? UpMbps : null, + DownMbps = DownMbps > 0 ? DownMbps : null, + HopInterval = HopInterval >= 5 ? HopInterval : null, + VmessSecurity = VmessSecurity.IsNullOrEmpty() ? null : VmessSecurity, + VlessEncryption = VlessEncryption.IsNullOrEmpty() ? null : VlessEncryption, + SsMethod = SsMethod.IsNullOrEmpty() ? null : SsMethod, + Username = Username.IsNullOrEmpty() ? null : Username, + WgPublicKey = WgPublicKey.IsNullOrEmpty() ? null : WgPublicKey, + WgInterfaceAddress = WgInterfaceAddress.IsNullOrEmpty() ? null : WgInterfaceAddress, + WgReserved = WgReserved.IsNullOrEmpty() ? null : WgReserved, + WgMtu = WgMtu >= 576 ? WgMtu : null, + }); if (await ConfigHandler.AddServer(_config, SelectedSource) == 0) { From 314fb7c73df6f98e59a95e2d0af3b29e1de12520 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 5 Feb 2026 13:15:04 +0800 Subject: [PATCH 14/20] Hy2 SalamanderPass --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 3 +- v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs | 18 +++++------ v2rayN/ServiceLib/Manager/AppManager.cs | 31 ++++++++++++------- v2rayN/ServiceLib/Models/ProtocolExtraItem.cs | 1 + .../Singbox/SingboxOutboundService.cs | 4 +-- .../CoreConfig/V2ray/V2rayOutboundService.cs | 7 ++--- .../ViewModels/AddServerViewModel.cs | 25 +++++++++------ .../Views/AddServerWindow.axaml.cs | 2 +- v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 2 +- 9 files changed, 54 insertions(+), 39 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index a8b4bf13..0262a2c2 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -707,7 +707,6 @@ public static class ConfigHandler profileItem.Address = profileItem.Address.TrimEx(); profileItem.Password = profileItem.Password.TrimEx(); - profileItem.Path = profileItem.Path.TrimEx(); profileItem.Network = string.Empty; if (profileItem.StreamSecurity.IsNullOrEmpty()) @@ -720,6 +719,7 @@ public static class ConfigHandler } profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with { + SalamanderPass = profileItem.GetProtocolExtra().SalamanderPass?.TrimEx(), UpMbps = profileItem.GetProtocolExtra().UpMbps is null or < 0 ? config.HysteriaItem.UpMbps : profileItem.GetProtocolExtra().UpMbps, DownMbps = profileItem.GetProtocolExtra().DownMbps is null or < 0 ? config.HysteriaItem.DownMbps : profileItem.GetProtocolExtra().DownMbps, HopInterval = profileItem.GetProtocolExtra().HopInterval is null or <= 5 ? Global.Hysteria2DefaultHopInt : profileItem.GetProtocolExtra().HopInterval, @@ -1119,6 +1119,7 @@ public static class ConfigHandler && AreEqual(o.Path, n.Path) && (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity) && AreEqual(oProtocolExtra.Flow, nProtocolExtra.Flow) + && AreEqual(oProtocolExtra.SalamanderPass, nProtocolExtra.SalamanderPass) && AreEqual(o.Sni, n.Sni) && AreEqual(o.Alpn, n.Alpn) && AreEqual(o.Fingerprint, n.Fingerprint) diff --git a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs index 59833e5d..401beda7 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs @@ -23,16 +23,15 @@ public class Hysteria2Fmt : BaseFmt var query = Utils.ParseQueryString(url.Query); ResolveUriQuery(query, ref item); - item.Path = GetQueryDecoded(query, "obfs-password"); if (item.CertSha.IsNullOrEmpty()) { item.CertSha = GetQueryDecoded(query, "pinSHA256"); } - ProtocolExtraItem extraItem = new() + item.SetProtocolExtra(item.GetProtocolExtra() with { - Ports = GetQueryDecoded(query, "mport") - }; - item.SetProtocolExtra(extraItem); + Ports = GetQueryDecoded(query, "mport"), + SalamanderPass = GetQueryDecoded(query, "obfs-password"), + }); return item; } @@ -53,15 +52,16 @@ public class Hysteria2Fmt : BaseFmt } var dicQuery = new Dictionary(); ToUriQueryLite(item, ref dicQuery); + var protocolExtraItem = item.GetProtocolExtra(); - if (item.Path.IsNotEmpty()) + if (!protocolExtraItem.SalamanderPass.IsNullOrEmpty()) { dicQuery.Add("obfs", "salamander"); - dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path)); + dicQuery.Add("obfs-password", Utils.UrlEncode(protocolExtraItem.SalamanderPass)); } - if (item.GetProtocolExtra()?.Ports?.IsNotEmpty() ?? false) + if (!protocolExtraItem.Ports.IsNullOrEmpty()) { - dicQuery.Add("mport", Utils.UrlEncode(item.GetProtocolExtra().Ports.Replace(':', '-'))); + dicQuery.Add("mport", Utils.UrlEncode(protocolExtraItem.Ports.Replace(':', '-'))); } if (!item.CertSha.IsNullOrEmpty()) { diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 7753cac8..867a4990 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -1,3 +1,5 @@ +using ServiceLib.Common; + namespace ServiceLib.Manager; public sealed class AppManager @@ -279,12 +281,7 @@ public sealed class AppManager foreach (var item in batch) { - ProtocolExtraItem extra = new() - { - AlterId = item.AlterId.ToString(), - Flow = item.Flow.IsNotEmpty() ? item.Flow : null, - Ports = item.Ports.IsNotEmpty() ? item.Ports : null, - }; + var extra = item.GetProtocolExtra(); if (item.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain) { @@ -304,14 +301,26 @@ public sealed class AppManager switch (item.ConfigType) { case EConfigType.Shadowsocks: - extra = extra with {SsMethod = item.Security.IsNotEmpty() ? item.Security : null}; + extra = extra with {SsMethod = item.Security.NullIfEmpty() }; break; case EConfigType.VMess: - extra = extra with {VmessSecurity = item.Security.IsNotEmpty() ? item.Security : null}; + extra = extra with + { + AlterId = item.AlterId.ToString(), + VmessSecurity = item.Security.NullIfEmpty(), + }; + break; + case EConfigType.VLESS: + extra = extra with + { + Flow = item.Flow.NullIfEmpty(), + }; break; case EConfigType.Hysteria2: extra = extra with { + SalamanderPass = item.Path.NullIfEmpty(), + Ports = item.Ports.NullIfEmpty(), UpMbps = _config.HysteriaItem.UpMbps, DownMbps = _config.HysteriaItem.DownMbps, HopInterval = _config.HysteriaItem.HopInterval @@ -320,9 +329,9 @@ public sealed class AppManager case EConfigType.WireGuard: extra = extra with { - WgPublicKey = item.PublicKey.IsNotEmpty() ? item.PublicKey : null, - WgInterfaceAddress = item.RequestHost.IsNotEmpty() ? item.RequestHost : null, - WgReserved = item.Path.IsNotEmpty() ? item.Path : null, + WgPublicKey = item.PublicKey.NullIfEmpty(), + WgInterfaceAddress = item.RequestHost.NullIfEmpty(), + WgReserved = item.Path.NullIfEmpty(), WgMtu = int.TryParse(item.ShortId, out var mtu) ? mtu : 1280 }; break; diff --git a/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs index 0233b542..09569537 100644 --- a/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs +++ b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs @@ -26,6 +26,7 @@ public record ProtocolExtraItem public int? WgMtu { get; init; } // hysteria2 + public string? SalamanderPass { get; init; } public int? UpMbps { get; init; } public int? DownMbps { get; init; } public string? Ports { get; init; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 4467425c..d6b88ed8 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -138,12 +138,12 @@ public partial class CoreConfigSingboxService { outbound.password = node.Password; - if (node.Path.IsNotEmpty()) + if (!protocolExtra.SalamanderPass.IsNullOrEmpty()) { outbound.obfs = new() { type = "salamander", - password = node.Path.TrimEx(), + password = protocolExtra.SalamanderPass.TrimEx(), }; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index b67df735..47fc3767 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -532,7 +532,7 @@ public partial class CoreConfigV2rayService interval = hopInterval, }; } - HysteriaSettings4Ray hysteriaSettings = new() + streamSettings.hysteriaSettings = new() { version = 2, auth = node.Password, @@ -540,8 +540,7 @@ public partial class CoreConfigV2rayService down = downMbps > 0 ? $"{downMbps}mbps" : null, udphop = udpHop, }; - streamSettings.hysteriaSettings = hysteriaSettings; - if (node.Path.IsNotEmpty()) + if (!protocolExtra.SalamanderPass.IsNullOrEmpty()) { streamSettings.finalmask ??= new(); streamSettings.finalmask.udp = @@ -549,7 +548,7 @@ public partial class CoreConfigV2rayService new Mask4Ray { type = "salamander", - settings = new MaskSettings4Ray { password = node.Path.TrimEx(), } + settings = new MaskSettings4Ray { password = protocolExtra.SalamanderPass.TrimEx(), } } ]; } diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index fd204969..4ba9c93f 100644 --- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -17,6 +17,9 @@ public class AddServerViewModel : MyReactiveObject [Reactive] public string CertSha { get; set; } + [Reactive] + public string SalamanderPass { get; set; } + [Reactive] public int AlterId { get; set; } @@ -109,6 +112,7 @@ public class AddServerViewModel : MyReactiveObject Ports = protocolExtra?.Ports ?? string.Empty; AlterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0; Flow = protocolExtra?.Flow ?? string.Empty; + SalamanderPass = protocolExtra?.SalamanderPass ?? string.Empty; UpMbps = protocolExtra?.UpMbps ?? 0; DownMbps = protocolExtra?.DownMbps ?? 0; HopInterval = protocolExtra?.HopInterval ?? Global.Hysteria2DefaultHopInt; @@ -168,19 +172,20 @@ public class AddServerViewModel : MyReactiveObject SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha; SelectedSource.SetProtocolExtra(SelectedSource.GetProtocolExtra() with { - Ports = Ports.IsNullOrEmpty() ? null : Ports, - AlterId = AlterId > 0 ? AlterId.ToString() : string.Empty, - Flow = Flow.IsNullOrEmpty() ? null : Flow, + Ports = Ports.NullIfEmpty(), + AlterId = AlterId > 0 ? AlterId.ToString() : null, + Flow = Flow.NullIfEmpty(), + SalamanderPass = SalamanderPass.NullIfEmpty(), UpMbps = UpMbps > 0 ? UpMbps : null, DownMbps = DownMbps > 0 ? DownMbps : null, HopInterval = HopInterval >= 5 ? HopInterval : null, - VmessSecurity = VmessSecurity.IsNullOrEmpty() ? null : VmessSecurity, - VlessEncryption = VlessEncryption.IsNullOrEmpty() ? null : VlessEncryption, - SsMethod = SsMethod.IsNullOrEmpty() ? null : SsMethod, - Username = Username.IsNullOrEmpty() ? null : Username, - WgPublicKey = WgPublicKey.IsNullOrEmpty() ? null : WgPublicKey, - WgInterfaceAddress = WgInterfaceAddress.IsNullOrEmpty() ? null : WgInterfaceAddress, - WgReserved = WgReserved.IsNullOrEmpty() ? null : WgReserved, + VmessSecurity = VmessSecurity.NullIfEmpty(), + VlessEncryption = VlessEncryption.NullIfEmpty(), + SsMethod = SsMethod.NullIfEmpty(), + Username = Username.NullIfEmpty(), + WgPublicKey = WgPublicKey.NullIfEmpty(), + WgInterfaceAddress = WgInterfaceAddress.NullIfEmpty(), + WgReserved = WgReserved.NullIfEmpty(), WgMtu = WgMtu >= 576 ? WgMtu : null, }); diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index c8e86ca9..63ce754b 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -144,7 +144,7 @@ public partial class AddServerWindow : WindowBase case EConfigType.Hysteria2: this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId7.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SalamanderPass, v => v.txtPath7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index fa105a8e..4a31f194 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -139,7 +139,7 @@ public partial class AddServerWindow case EConfigType.Hysteria2: this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId7.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SalamanderPass, v => v.txtPath7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables); From fa8c3f337cc67cc9e1bfc7e20747c397eb41d1ef Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 5 Feb 2026 15:28:02 +0800 Subject: [PATCH 15/20] Fix Tuic --- v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs | 2 +- v2rayN/ServiceLib/Manager/AppManager.cs | 8 ++++++++ .../Services/CoreConfig/Singbox/SingboxOutboundService.cs | 4 ++-- v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs | 4 ++-- v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 4 ++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs index b17b1ef2..9532001c 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs @@ -53,6 +53,6 @@ public class TuicFmt : BaseFmt dicQuery.Add("congestion_control", item.HeaderType); - return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Password}:{item.GetProtocolExtra().Username ?? ""}", dicQuery, remark); + return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.GetProtocolExtra().Username ?? ""}:{item.Password}", dicQuery, remark); } } diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 867a4990..034dbce4 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -326,6 +326,14 @@ public sealed class AppManager HopInterval = _config.HysteriaItem.HopInterval }; break; + case EConfigType.TUIC: + extra = extra with + { + Username = item.Id, + }; + item.Id = item.Security; + item.Password = item.Security; + break; case EConfigType.WireGuard: extra = extra with { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index d6b88ed8..127b09a5 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -175,8 +175,8 @@ public partial class CoreConfigSingboxService } case EConfigType.TUIC: { - outbound.uuid = node.Password; - outbound.password = protocolExtra.Username; + outbound.uuid = protocolExtra.Username; + outbound.password = node.Password; outbound.congestion_control = node.HeaderType; break; } diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index 63ce754b..e5f96a10 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -152,8 +152,8 @@ public partial class AddServerWindow : WindowBase break; case EConfigType.TUIC: - this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId8.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.Username, v => v.txtSecurity8.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Username, v => v.txtId8.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables); break; diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index 4a31f194..c3c403c0 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -147,8 +147,8 @@ public partial class AddServerWindow break; case EConfigType.TUIC: - this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId8.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.Username, v => v.txtSecurity8.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Username, v => v.txtId8.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.Text).DisposeWith(disposables); break; From 1b79d2f56040eb98e2ad2d4446ebc8043e38903a Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 5 Feb 2026 16:23:50 +0800 Subject: [PATCH 16/20] Fix group --- .../ServiceLib/Manager/GroupProfileManager.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/v2rayN/ServiceLib/Manager/GroupProfileManager.cs b/v2rayN/ServiceLib/Manager/GroupProfileManager.cs index ce7341dc..3c9d766e 100644 --- a/v2rayN/ServiceLib/Manager/GroupProfileManager.cs +++ b/v2rayN/ServiceLib/Manager/GroupProfileManager.cs @@ -72,21 +72,30 @@ public class GroupProfileManager public static async Task<(List Items, ProtocolExtraItem? Extra)> GetChildProfileItems(ProfileItem profileItem) { var protocolExtra = profileItem?.GetProtocolExtra(); - var items = await GetChildProfileItems(protocolExtra); + return (await GetChildProfileItemsByProtocolExtra(protocolExtra), protocolExtra); + } + + public static async Task> GetChildProfileItemsByProtocolExtra(ProtocolExtraItem? protocolExtra) + { + if (protocolExtra == null) + { + return new(); + } + var items = await GetSelectedChildProfileItems(protocolExtra); var subItems = await GetSubChildProfileItems(protocolExtra); items.AddRange(subItems); - return (items, protocolExtra); + return items; } - public static async Task> GetChildProfileItems(ProtocolExtraItem? extra) + public static async Task> GetSelectedChildProfileItems(ProtocolExtraItem? extra) { if (extra == null || extra.ChildItems.IsNullOrEmpty()) { return new(); } var childProfiles = (await Task.WhenAll( - Utils.String2List(extra.ChildItems) + (Utils.String2List(extra.ChildItems) ?? new()) .Where(p => !p.IsNullOrEmpty()) .Select(AppManager.Instance.GetProfileItem) )) @@ -101,19 +110,19 @@ public class GroupProfileManager public static async Task> GetSubChildProfileItems(ProtocolExtraItem? extra) { - if (extra == null || extra.Filter.IsNullOrEmpty()) + if (extra == null || extra.SubChildItems.IsNullOrEmpty()) { return new(); } var childProfiles = await AppManager.Instance.ProfileItems(extra.SubChildItems ?? string.Empty); - return childProfiles.Where(p => + return childProfiles?.Where(p => p != null && p.IsValid() && !p.ConfigType.IsComplexType() && (extra.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, extra.Filter)) ) - .ToList(); + .ToList() ?? new(); } public static async Task> GetAllChildDomainAddresses(ProfileItem profileItem) From ad685fcc0380c0d5c860b3aab09c975516e40629 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 5 Feb 2026 17:32:31 +0800 Subject: [PATCH 17/20] Fix Tuic --- v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs index 9532001c..ec38c501 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs @@ -24,8 +24,8 @@ public class TuicFmt : BaseFmt var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); if (userInfoParts.Length == 2) { - item.Password = userInfoParts.First(); - item.SetProtocolExtra(item.GetProtocolExtra() with { Username = userInfoParts.Last() }); + item.SetProtocolExtra(item.GetProtocolExtra() with { Username = userInfoParts.First() }); + item.Password = userInfoParts.Last(); } var query = Utils.ParseQueryString(url.Query); From 6d953669712d45ef6513ae01bb72eed5b3c090f8 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 5 Feb 2026 17:44:41 +0800 Subject: [PATCH 18/20] Fix hy2 Brutal Bandwidth --- .../Services/CoreConfig/Singbox/SingboxOutboundService.cs | 4 ++-- .../Services/CoreConfig/V2ray/V2rayOutboundService.cs | 4 ++-- v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 127b09a5..6ce5f6d2 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -149,10 +149,10 @@ public partial class CoreConfigSingboxService outbound.up_mbps = protocolExtra?.UpMbps is { } su and >= 0 ? su - : 0; + : _config.HysteriaItem.UpMbps; outbound.down_mbps = protocolExtra?.DownMbps is { } sd and >= 0 ? sd - : 0; + : _config.HysteriaItem.DownMbps; var ports = protocolExtra?.Ports?.IsNullOrEmpty() == false ? protocolExtra.Ports : null; if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index 47fc3767..bdb99699 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -515,10 +515,10 @@ public partial class CoreConfigV2rayService var ports = protocolExtra?.Ports; int? upMbps = protocolExtra?.UpMbps is { } su and >= 0 ? su - : 0; + : _config.HysteriaItem.UpMbps; int? downMbps = protocolExtra?.DownMbps is { } sd and >= 0 ? sd - : 0; + : _config.HysteriaItem.UpMbps; var hopInterval = protocolExtra?.HopInterval is { } hi and >= 5 ? hi : _config.HysteriaItem.HopInterval >= 5 ? _config.HysteriaItem.HopInterval : Global.Hysteria2DefaultHopInt; diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index 4ba9c93f..73915544 100644 --- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -113,8 +113,8 @@ public class AddServerViewModel : MyReactiveObject AlterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0; Flow = protocolExtra?.Flow ?? string.Empty; SalamanderPass = protocolExtra?.SalamanderPass ?? string.Empty; - UpMbps = protocolExtra?.UpMbps ?? 0; - DownMbps = protocolExtra?.DownMbps ?? 0; + UpMbps = protocolExtra?.UpMbps ?? _config.HysteriaItem.UpMbps; + DownMbps = protocolExtra?.DownMbps ?? _config.HysteriaItem.DownMbps; HopInterval = protocolExtra?.HopInterval ?? Global.Hysteria2DefaultHopInt; VmessSecurity = protocolExtra?.VmessSecurity?.IsNullOrEmpty() == false ? protocolExtra.VmessSecurity : Global.DefaultSecurity; VlessEncryption = protocolExtra?.VlessEncryption.IsNullOrEmpty() == false ? protocolExtra.VlessEncryption : Global.None; @@ -176,8 +176,8 @@ public class AddServerViewModel : MyReactiveObject AlterId = AlterId > 0 ? AlterId.ToString() : null, Flow = Flow.NullIfEmpty(), SalamanderPass = SalamanderPass.NullIfEmpty(), - UpMbps = UpMbps > 0 ? UpMbps : null, - DownMbps = DownMbps > 0 ? DownMbps : null, + UpMbps = UpMbps >= 0 ? UpMbps : null, + DownMbps = DownMbps >= 0 ? DownMbps : null, HopInterval = HopInterval >= 5 ? HopInterval : null, VmessSecurity = VmessSecurity.NullIfEmpty(), VlessEncryption = VlessEncryption.NullIfEmpty(), From e2cd3069f0a538680b3d7d55f61e7b7833348281 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 5 Feb 2026 17:46:30 +0800 Subject: [PATCH 19/20] Clean Code --- v2rayN/ServiceLib/Manager/AppManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 034dbce4..9cd5a567 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -1,5 +1,3 @@ -using ServiceLib.Common; - namespace ServiceLib.Manager; public sealed class AppManager From fe33c0e0e675fefc67e9a3d1db582d3abb9e8a01 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 5 Feb 2026 18:02:17 +0800 Subject: [PATCH 20/20] Fix --- v2rayN/ServiceLib/Manager/AppManager.cs | 95 ++++++++++--------- .../Singbox/SingboxOutboundService.cs | 6 +- .../CoreConfig/V2ray/V2rayOutboundService.cs | 5 +- 3 files changed, 54 insertions(+), 52 deletions(-) diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 9cd5a567..1b7ed58c 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -295,53 +295,56 @@ public sealed class AppManager MultipleLoad = groupItem.MultipleLoad, }; } + } - switch (item.ConfigType) - { - case EConfigType.Shadowsocks: - extra = extra with {SsMethod = item.Security.NullIfEmpty() }; - break; - case EConfigType.VMess: - extra = extra with - { - AlterId = item.AlterId.ToString(), - VmessSecurity = item.Security.NullIfEmpty(), - }; - break; - case EConfigType.VLESS: - extra = extra with - { - Flow = item.Flow.NullIfEmpty(), - }; - break; - case EConfigType.Hysteria2: - extra = extra with - { - SalamanderPass = item.Path.NullIfEmpty(), - Ports = item.Ports.NullIfEmpty(), - UpMbps = _config.HysteriaItem.UpMbps, - DownMbps = _config.HysteriaItem.DownMbps, - HopInterval = _config.HysteriaItem.HopInterval - }; - break; - case EConfigType.TUIC: - extra = extra with - { - Username = item.Id, - }; - item.Id = item.Security; - item.Password = item.Security; - break; - case EConfigType.WireGuard: - extra = extra with - { - WgPublicKey = item.PublicKey.NullIfEmpty(), - WgInterfaceAddress = item.RequestHost.NullIfEmpty(), - WgReserved = item.Path.NullIfEmpty(), - WgMtu = int.TryParse(item.ShortId, out var mtu) ? mtu : 1280 - }; - break; - } + switch (item.ConfigType) + { + case EConfigType.Shadowsocks: + extra = extra with { SsMethod = item.Security.NullIfEmpty() }; + break; + case EConfigType.VMess: + extra = extra with + { + AlterId = item.AlterId.ToString(), + VmessSecurity = item.Security.NullIfEmpty(), + }; + break; + case EConfigType.VLESS: + extra = extra with + { + Flow = item.Flow.NullIfEmpty(), + VlessEncryption = item.Security, + }; + break; + case EConfigType.Hysteria2: + extra = extra with + { + SalamanderPass = item.Path.NullIfEmpty(), + Ports = item.Ports.NullIfEmpty(), + UpMbps = _config.HysteriaItem.UpMbps, + DownMbps = _config.HysteriaItem.DownMbps, + HopInterval = _config.HysteriaItem.HopInterval + }; + break; + case EConfigType.TUIC: + extra = extra with + { + Username = item.Id, + }; + item.Id = item.Security; + item.Password = item.Security; + break; + case EConfigType.WireGuard: + extra = extra with + { + WgPublicKey = item.PublicKey.NullIfEmpty(), + WgInterfaceAddress = item.RequestHost.NullIfEmpty(), + WgReserved = item.Path.NullIfEmpty(), + WgMtu = int.TryParse(item.ShortId, out var mtu) ? mtu : 1280 + }; + break; + default: + break; } item.SetProtocolExtra(extra); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 6ce5f6d2..ef4bbaec 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -114,13 +114,13 @@ public partial class CoreConfigSingboxService outbound.packet_encoding = "xudp"; - if (protocolExtra.Flow.IsNullOrEmpty()) + if (!protocolExtra.Flow.IsNullOrEmpty()) { - await GenOutboundMux(node, outbound); + outbound.flow = protocolExtra.Flow; } else { - outbound.flow = protocolExtra.Flow; + await GenOutboundMux(node, outbound); } await GenOutboundTransport(node, outbound); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index bdb99699..71d27ea1 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -144,13 +144,12 @@ public partial class CoreConfigV2rayService usersItem.email = Global.UserEMail; usersItem.encryption = protocolExtra.VlessEncryption; - if (protocolExtra.Flow.IsNullOrEmpty()) + if (!protocolExtra.Flow.IsNullOrEmpty()) { - await GenOutboundMux(node, outbound, muxEnabled, muxEnabled); + usersItem.flow = protocolExtra.Flow; } else { - usersItem.flow = protocolExtra.Flow; await GenOutboundMux(node, outbound, false, muxEnabled); } outbound.settings.servers = null;