diff --git a/v2rayN/v2rayN/Handler/CoreConfigHandler.cs b/v2rayN/v2rayN/Handler/CoreConfigHandler.cs index 86617d8c..b2ab8f31 100644 --- a/v2rayN/v2rayN/Handler/CoreConfigHandler.cs +++ b/v2rayN/v2rayN/Handler/CoreConfigHandler.cs @@ -179,8 +179,9 @@ namespace v2rayN.Handler return inbound; } - private static int routing(Config config, ref V2rayConfig v2rayConfig) + private static int routing(Config config, ref V2rayConfig v2rayConfig, out HashSet usedOutboundTags) { + usedOutboundTags = new HashSet(); try { if (v2rayConfig.routing != null @@ -204,6 +205,7 @@ namespace v2rayN.Handler if (item.enabled) { routingUserRule(item, ref v2rayConfig); + usedOutboundTags.Add(item.outboundTag); } } } @@ -302,12 +304,32 @@ namespace v2rayN.Handler return 0; } - private static int outbound(ProfileItem node, ref V2rayConfig v2rayConfig) + private static Outbounds createOutbound(ProfileItem node) { + var outbound = new Outbounds + { + tag = node.remarks, + protocol = node.configType switch + { + EConfigType.VMess => Global.vmessProtocolLite, + EConfigType.Shadowsocks => Global.ssProtocolLite, + EConfigType.VLESS => Global.vlessProtocolLite, + EConfigType.Trojan => Global.trojanProtocolLite, + EConfigType.Socks => Global.socksProtocolLite, + _ => "unknown" + }, + settings = new Outboundsettings + { + vnext = new List { new VnextItem { users = new List() } }, + servers = new List(), + response = new Response() + }, + streamSettings = new StreamSettings(), + mux = new Mux() + }; try { var config = LazyConfig.Instance.GetConfig(); - Outbounds outbound = v2rayConfig.outbounds[0]; if (node.configType == EConfigType.VMess) { VnextItem vnextItem; @@ -534,6 +556,26 @@ namespace v2rayN.Handler { Utils.SaveLog(ex.Message, ex); } + return outbound; + } + + private static int outbound(ProfileItem node, ref V2rayConfig v2rayConfig) + { + try + { + if (v2rayConfig.outbounds.Count <= 0) + { + v2rayConfig.outbounds.Add(createOutbound(node)); + } + else + { + v2rayConfig.outbounds[0] = createOutbound(node); + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } return 0; } @@ -956,7 +998,7 @@ namespace v2rayN.Handler return 0; } - public static int GenerateClientConfigContent(ProfileItem node, bool blExport, ref V2rayConfig v2rayConfig, out string msg) + private static int GenerateClientConfigContent(ProfileItem node, bool blExport, ref V2rayConfig v2rayConfig, out string msg) { try { @@ -988,10 +1030,20 @@ namespace v2rayN.Handler inbound(config, ref v2rayConfig); - routing(config, ref v2rayConfig); + routing(config, ref v2rayConfig, out HashSet usedTags); + v2rayConfig.outbounds.Clear(); //outbound outbound(node, ref v2rayConfig); + // first outbound is current active node, of which name is always "proxy" + v2rayConfig.outbounds[0].tag = Global.agentTag; + + // append all outbound used by custom routing rules + var allOutbounds = SqliteHelper.Instance.Table().Where(i => usedTags.Contains(i.remarks)); + foreach (var item in allOutbounds) + { + v2rayConfig.outbounds.Add(createOutbound(item)); + } //dns dns(config, ref v2rayConfig); diff --git a/v2rayN/v2rayN/Mode/ProfileItem.cs b/v2rayN/v2rayN/Mode/ProfileItem.cs index 00ce76c1..658c972e 100644 --- a/v2rayN/v2rayN/Mode/ProfileItem.cs +++ b/v2rayN/v2rayN/Mode/ProfileItem.cs @@ -247,5 +247,56 @@ namespace v2rayN.Mode public string fingerprint { get; set; } public bool displayLog { get; set; } = true; + + // TODO: change ProfileItem and other models to C# records + public override bool Equals(object? obj) { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((ProfileItem)obj); + } + + protected bool Equals(ProfileItem other) { + return indexId == other.indexId && configType == other.configType && configVersion == other.configVersion && + sort == other.sort && address == other.address && port == other.port && id == other.id && + alterId == other.alterId && security == other.security && network == other.network && + remarks == other.remarks && headerType == other.headerType && requestHost == other.requestHost && + path == other.path && streamSecurity == other.streamSecurity && allowInsecure == other.allowInsecure && + delay == other.delay && speed == other.speed && subid == other.subid && isSub == other.isSub && + flow == other.flow && sni == other.sni && alpn == other.alpn && coreType == other.coreType && + preSocksPort == other.preSocksPort && fingerprint == other.fingerprint && displayLog == other.displayLog; + } + + public override int GetHashCode() { + var hashCode = new HashCode(); + hashCode.Add(indexId); + hashCode.Add((int)configType); + hashCode.Add(configVersion); + hashCode.Add(sort); + hashCode.Add(address); + hashCode.Add(port); + hashCode.Add(id); + hashCode.Add(alterId); + hashCode.Add(security); + hashCode.Add(network); + hashCode.Add(remarks); + hashCode.Add(headerType); + hashCode.Add(requestHost); + hashCode.Add(path); + hashCode.Add(streamSecurity); + hashCode.Add(allowInsecure); + hashCode.Add(delay); + hashCode.Add(speed); + hashCode.Add(subid); + hashCode.Add(isSub); + hashCode.Add(flow); + hashCode.Add(sni); + hashCode.Add(alpn); + hashCode.Add(coreType); + hashCode.Add(preSocksPort); + hashCode.Add(fingerprint); + hashCode.Add(displayLog); + return hashCode.ToHashCode(); + } } } diff --git a/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs index 73e4cf7c..03d63b34 100644 --- a/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs @@ -20,6 +20,10 @@ namespace v2rayN.Views cmbOutboundTag.Items.Add(Global.agentTag); cmbOutboundTag.Items.Add(Global.directTag); cmbOutboundTag.Items.Add(Global.blockTag); + foreach (var profileItem in SqliteHelper.Instance.Table()) + { + cmbOutboundTag.Items.Add(profileItem.remarks); + } Global.Protocols.ForEach(it => { clbProtocol.Items.Add(it);