mirror of
https://github.com/2dust/v2rayN.git
synced 2025-07-01 20:42:10 +00:00
Compare commits
3 commits
3facccfb74
...
a664ce71b3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a664ce71b3 | ||
![]() |
84f93f2ae6 | ||
![]() |
d10c9da7c3 |
12 changed files with 805 additions and 732 deletions
919
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
919
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -1131,6 +1131,15 @@
|
|||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>فعال کردن فرگمنت</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentInterval" xml:space="preserve">
|
||||
<value>Fragment Interval</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentLength" xml:space="preserve">
|
||||
<value>Fragment Length</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentPackets" xml:space="preserve">
|
||||
<value>Fragment Packets</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value>
|
||||
</data>
|
||||
|
|
|
@ -1131,6 +1131,15 @@
|
|||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>Fragmentum engedélyezése</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentInterval" xml:space="preserve">
|
||||
<value>Fragment Interval</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentLength" xml:space="preserve">
|
||||
<value>Fragment Length</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentPackets" xml:space="preserve">
|
||||
<value>Fragment Packets</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>Cache fájl engedélyezése a sing-box számára (szabálykészlet fájlok)</value>
|
||||
</data>
|
||||
|
|
|
@ -1131,6 +1131,15 @@
|
|||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>Enable fragment</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentInterval" xml:space="preserve">
|
||||
<value>Fragment Interval</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentLength" xml:space="preserve">
|
||||
<value>Fragment Length</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentPackets" xml:space="preserve">
|
||||
<value>Fragment Packets</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>Enable cache file for sing-box (ruleset files)</value>
|
||||
</data>
|
||||
|
|
|
@ -816,9 +816,6 @@
|
|||
<data name="menuMoveUp" xml:space="preserve">
|
||||
<value>Вверх (U)</value>
|
||||
</data>
|
||||
<data name="menuMoveTo" xml:space="preserve">
|
||||
<value>Переместить вверх/вниз</value>
|
||||
</data>
|
||||
<data name="MsgFilterTitle" xml:space="preserve">
|
||||
<value>Фильтр, поддерживает regex</value>
|
||||
</data>
|
||||
|
@ -990,6 +987,9 @@
|
|||
<data name="TbSettingsSpeedTestUrl" xml:space="preserve">
|
||||
<value>URL для тестирования скорости</value>
|
||||
</data>
|
||||
<data name="menuMoveTo" xml:space="preserve">
|
||||
<value>Переместить вверх/вниз</value>
|
||||
</data>
|
||||
<data name="TbPublicKey" xml:space="preserve">
|
||||
<value>PublicKey</value>
|
||||
</data>
|
||||
|
@ -1131,6 +1131,15 @@
|
|||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>Включить фрагментацию (Fragment)</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentInterval" xml:space="preserve">
|
||||
<value>Fragment Interval</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentLength" xml:space="preserve">
|
||||
<value>Fragment Length</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentPackets" xml:space="preserve">
|
||||
<value>Fragment Packets</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>Включить файл кэша для sing-box (файлы наборов правил)</value>
|
||||
</data>
|
||||
|
|
|
@ -1128,6 +1128,15 @@
|
|||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>启用分片(Fragment)</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentInterval" xml:space="preserve">
|
||||
<value>Fragment Interval</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentLength" xml:space="preserve">
|
||||
<value>Fragment Length</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentPackets" xml:space="preserve">
|
||||
<value>Fragment Packets</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>启用 sing-box(规则集文件)的缓存文件</value>
|
||||
</data>
|
||||
|
|
|
@ -1128,6 +1128,15 @@
|
|||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>啟用分片(Fragment)</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentInterval" xml:space="preserve">
|
||||
<value>Fragment Interval</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentLength" xml:space="preserve">
|
||||
<value>Fragment Length</value>
|
||||
</data>
|
||||
<data name="TbSettingsFragmentPackets" xml:space="preserve">
|
||||
<value>Fragment Packets</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>啟用 sing-box(規則集檔案)的快取檔案</value>
|
||||
</data>
|
||||
|
|
|
@ -918,29 +918,21 @@ public class CoreConfigSingboxService
|
|||
|
||||
//Previous proxy
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
string? prevOutboundTag = null;
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom)
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevOutbound.tag = $"{Global.ProxyTag}2";
|
||||
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
||||
prevOutbound.tag = prevOutboundTag;
|
||||
singboxConfig.outbounds.Add(prevOutbound);
|
||||
|
||||
outbound.detour = prevOutbound.tag;
|
||||
}
|
||||
var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
|
||||
|
||||
//Next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom)
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
var nextOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
nextOutbound.tag = Global.ProxyTag;
|
||||
singboxConfig.outbounds.Insert(0, nextOutbound);
|
||||
|
||||
outbound.tag = $"{Global.ProxyTag}1";
|
||||
nextOutbound.detour = outbound.tag;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -967,8 +959,8 @@ public class CoreConfigSingboxService
|
|||
var proxyTags = new List<string>(); // For selector and urltest outbounds
|
||||
|
||||
// Cache for chain proxies to avoid duplicate generation
|
||||
var chainProxyCache = new Dictionary<string, (string?, Outbound4Sbox?)>();
|
||||
var prevProxyTags = new Dictionary<string, string>(); // Map from profile name to tag
|
||||
var nextProxyCache = new Dictionary<string, Outbound4Sbox?>();
|
||||
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
||||
int prevIndex = 0; // Index for prev outbounds
|
||||
|
||||
// Process each node
|
||||
|
@ -977,112 +969,55 @@ public class CoreConfigSingboxService
|
|||
{
|
||||
index++;
|
||||
|
||||
// Skip unsupported config types
|
||||
if (node.ConfigType is EConfigType.Custom)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle proxy chain
|
||||
string? prevTag = null;
|
||||
Outbound4Sbox? nextOutbound = null;
|
||||
|
||||
if (node.Subid.IsNotEmpty())
|
||||
var currentOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null);
|
||||
if (nextOutbound != null)
|
||||
{
|
||||
// Check if chain proxy is already cached
|
||||
if (chainProxyCache.TryGetValue(node.Subid, out var chainProxy))
|
||||
{
|
||||
prevTag = chainProxy.Item1;
|
||||
nextOutbound = chainProxy.Item2;
|
||||
nextOutbound = JsonUtils.DeepCopy(nextOutbound);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate chain proxy and cache it
|
||||
|
||||
var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
|
||||
if (subItem != null)
|
||||
|
||||
// current proxy
|
||||
await GenOutbound(node, currentOutbound);
|
||||
currentOutbound.tag = $"{Global.ProxyTag}-{index}";
|
||||
proxyTags.Add(currentOutbound.tag);
|
||||
|
||||
if (!node.Subid.IsNullOrEmpty())
|
||||
{
|
||||
// Process previous proxy
|
||||
if (!subItem.PrevProfile.IsNullOrEmpty())
|
||||
if (prevProxyTags.TryGetValue(node.Subid, out var value))
|
||||
{
|
||||
// Check if this previous proxy was already created
|
||||
if (prevProxyTags.TryGetValue(subItem.PrevProfile, out var existingTag))
|
||||
{
|
||||
prevTag = existingTag;
|
||||
prevTag = value; // maybe null
|
||||
}
|
||||
else
|
||||
{
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode != null && prevNode.ConfigType != EConfigType.Custom)
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom)
|
||||
{
|
||||
prevIndex++;
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
|
||||
prevTag = $"{Global.ProxyTag}-prev-{prevIndex}";
|
||||
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
||||
prevOutbound.tag = prevTag;
|
||||
prevProxyTags[subItem.PrevProfile] = prevTag;
|
||||
|
||||
// Add to prev outbounds list (will be added at the end)
|
||||
prevOutbounds.Add(prevOutbound);
|
||||
}
|
||||
}
|
||||
prevProxyTags[node.Subid] = prevTag;
|
||||
}
|
||||
|
||||
// Process next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode != null && nextNode.ConfigType != EConfigType.Custom)
|
||||
nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound);
|
||||
if (!nextProxyCache.ContainsKey(node.Subid))
|
||||
{
|
||||
nextOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
}
|
||||
|
||||
// Cache the chain proxy
|
||||
chainProxyCache[node.Subid] = (prevTag, nextOutbound);
|
||||
}
|
||||
nextProxyCache[node.Subid] = nextOutbound;
|
||||
}
|
||||
}
|
||||
|
||||
// Create main outbound
|
||||
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
|
||||
await GenOutbound(node, outbound);
|
||||
outbound.tag = $"{Global.ProxyTag}-{index}";
|
||||
|
||||
// Configure proxy chain relationships
|
||||
if (nextOutbound != null)
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
// If there's a next proxy, it should be the final outbound in the chain
|
||||
var originalTag = outbound.tag;
|
||||
outbound.tag = $"mid-{Global.ProxyTag}-{index}";
|
||||
|
||||
var nextOutboundCopy = JsonUtils.DeepCopy(nextOutbound);
|
||||
nextOutboundCopy.tag = originalTag;
|
||||
nextOutboundCopy.detour = outbound.tag; // Use detour instead of sockopt
|
||||
|
||||
if (prevTag != null)
|
||||
{
|
||||
outbound.detour = prevTag;
|
||||
}
|
||||
|
||||
// Add to proxy tags for selector/urltest
|
||||
proxyTags.Add(originalTag);
|
||||
|
||||
// Add in reverse order to ensure final outbound is added first
|
||||
resultOutbounds.Add(nextOutboundCopy); // Final outbound (exposed to internet)
|
||||
resultOutbounds.Add(outbound); // Middle outbound
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no next proxy, the main outbound is the final one
|
||||
if (prevTag != null)
|
||||
{
|
||||
outbound.detour = prevTag;
|
||||
}
|
||||
|
||||
// Add to proxy tags for selector/urltest
|
||||
proxyTags.Add(outbound.tag);
|
||||
resultOutbounds.Add(outbound);
|
||||
resultOutbounds.Add(nextOutbound);
|
||||
}
|
||||
resultOutbounds.Add(currentOutbound);
|
||||
}
|
||||
|
||||
// Add urltest outbound (auto selection based on latency)
|
||||
|
@ -1124,6 +1059,53 @@ public class CoreConfigSingboxService
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a chained outbound configuration for the given subItem and outbound.
|
||||
/// The outbound's tag must be set before calling this method.
|
||||
/// Returns the next proxy's outbound configuration, which may be null if no next proxy exists.
|
||||
/// </summary>
|
||||
/// <param name="subItem">The subscription item containing proxy chain information.</param>
|
||||
/// <param name="outbound">The current outbound configuration. Its tag must be set before calling this method.</param>
|
||||
/// <param name="prevOutboundTag">The tag of the previous outbound in the chain, if any.</param>
|
||||
/// <param name="nextOutbound">The outbound for the next proxy in the chain, if already created. If null, will be created inside.</param>
|
||||
/// <returns>
|
||||
/// The outbound configuration for the next proxy in the chain, or null if no next proxy exists.
|
||||
/// </returns>
|
||||
private async Task<Outbound4Sbox?> GenChainOutbounds(SubItem subItem, Outbound4Sbox outbound, string? prevOutboundTag, Outbound4Sbox? nextOutbound = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||
|
||||
if (!prevOutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
outbound.detour = prevOutboundTag;
|
||||
}
|
||||
|
||||
// Next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom)
|
||||
{
|
||||
if (nextOutbound == null)
|
||||
{
|
||||
nextOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
}
|
||||
nextOutbound.tag = outbound.tag;
|
||||
|
||||
outbound.tag = $"mid-{outbound.tag}";
|
||||
nextOutbound.detour = outbound.tag;
|
||||
}
|
||||
return nextOutbound;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<int> GenRouting(SingboxConfig singboxConfig)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -1318,6 +1318,7 @@ public class CoreConfigV2rayService
|
|||
|
||||
//Previous proxy
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
string? prevOutboundTag = null;
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
|
@ -1325,32 +1326,15 @@ public class CoreConfigV2rayService
|
|||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevOutbound.tag = $"{Global.ProxyTag}2";
|
||||
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
||||
prevOutbound.tag = prevOutboundTag;
|
||||
v2rayConfig.outbounds.Add(prevOutbound);
|
||||
|
||||
outbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = prevOutbound.tag
|
||||
};
|
||||
}
|
||||
var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
|
||||
|
||||
//Next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC)
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
var nextOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
nextOutbound.tag = Global.ProxyTag;
|
||||
v2rayConfig.outbounds.Insert(0, nextOutbound);
|
||||
|
||||
outbound.tag = $"{Global.ProxyTag}1";
|
||||
nextOutbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = outbound.tag
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -1375,31 +1359,9 @@ public class CoreConfigV2rayService
|
|||
var resultOutbounds = new List<Outbounds4Ray>();
|
||||
var prevOutbounds = new List<Outbounds4Ray>(); // Separate list for prev outbounds and fragment
|
||||
|
||||
// Handle fragment outbound
|
||||
Outbounds4Ray? fragmentOutbound = null;
|
||||
if (_config.CoreBasicItem.EnableFragment)
|
||||
{
|
||||
fragmentOutbound = new Outbounds4Ray
|
||||
{
|
||||
protocol = "freedom",
|
||||
tag = $"fragment-{Global.ProxyTag}",
|
||||
settings = new()
|
||||
{
|
||||
fragment = new()
|
||||
{
|
||||
packets = _config.Fragment4RayItem?.Packets,
|
||||
length = _config.Fragment4RayItem?.Length,
|
||||
interval = _config.Fragment4RayItem?.Interval
|
||||
}
|
||||
}
|
||||
};
|
||||
// Add to prevOutbounds instead of v2rayConfig.outbounds
|
||||
prevOutbounds.Add(fragmentOutbound);
|
||||
}
|
||||
|
||||
// Cache for chain proxies to avoid duplicate generation
|
||||
var chainProxyCache = new Dictionary<string, (string?, Outbounds4Ray?)>();
|
||||
var prevProxyTags = new Dictionary<string, string>(); // Map from profile name to tag
|
||||
var nextProxyCache = new Dictionary<string, Outbounds4Ray?>();
|
||||
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
||||
int prevIndex = 0; // Index for prev outbounds
|
||||
|
||||
// Process nodes
|
||||
|
@ -1408,126 +1370,56 @@ public class CoreConfigV2rayService
|
|||
{
|
||||
index++;
|
||||
|
||||
// Skip unsupported config types
|
||||
if (node.ConfigType is EConfigType.Custom or EConfigType.Hysteria2 or EConfigType.TUIC)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle proxy chain
|
||||
string? prevTag = null;
|
||||
Outbounds4Ray? nextOutbound = null;
|
||||
var currentOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null);
|
||||
if (nextOutbound != null)
|
||||
{
|
||||
nextOutbound = JsonUtils.DeepCopy(nextOutbound);
|
||||
}
|
||||
|
||||
var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
|
||||
|
||||
// current proxy
|
||||
await GenOutbound(node, currentOutbound);
|
||||
currentOutbound.tag = $"{Global.ProxyTag}-{index}";
|
||||
|
||||
if (!node.Subid.IsNullOrEmpty())
|
||||
{
|
||||
// Check if chain proxy is already cached
|
||||
if (chainProxyCache.TryGetValue(node.Subid, out var chainProxy))
|
||||
if (prevProxyTags.TryGetValue(node.Subid, out var value))
|
||||
{
|
||||
prevTag = chainProxy.Item1;
|
||||
nextOutbound = chainProxy.Item2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate chain proxy and cache it
|
||||
var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
|
||||
if (subItem != null)
|
||||
{
|
||||
// Process previous proxy
|
||||
if (!subItem.PrevProfile.IsNullOrEmpty())
|
||||
{
|
||||
// Check if this previous proxy was already created
|
||||
if (prevProxyTags.TryGetValue(subItem.PrevProfile, out var existingTag))
|
||||
{
|
||||
prevTag = existingTag;
|
||||
prevTag = value; // maybe null
|
||||
}
|
||||
else
|
||||
{
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode != null
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC)
|
||||
{
|
||||
prevIndex++;
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
|
||||
prevTag = $"{Global.ProxyTag}-prev-{prevIndex}";
|
||||
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
||||
prevOutbound.tag = prevTag;
|
||||
prevProxyTags[subItem.PrevProfile] = prevTag;
|
||||
|
||||
// Set fragment if needed
|
||||
if (fragmentOutbound != null && prevOutbound.streamSettings?.security.IsNullOrEmpty() == false)
|
||||
{
|
||||
prevOutbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = fragmentOutbound.tag
|
||||
};
|
||||
}
|
||||
|
||||
// Add to prev outbounds list (will be added at the end)
|
||||
prevOutbounds.Add(prevOutbound);
|
||||
}
|
||||
}
|
||||
prevProxyTags[node.Subid] = prevTag;
|
||||
}
|
||||
|
||||
// Process next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode != null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC)
|
||||
nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound);
|
||||
if (!nextProxyCache.ContainsKey(node.Subid))
|
||||
{
|
||||
nextOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
}
|
||||
|
||||
// Cache the chain proxy
|
||||
chainProxyCache[node.Subid] = (prevTag, nextOutbound);
|
||||
}
|
||||
nextProxyCache[node.Subid] = nextOutbound;
|
||||
}
|
||||
}
|
||||
|
||||
// Create main outbound
|
||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
|
||||
await GenOutbound(node, outbound);
|
||||
outbound.tag = $"{Global.ProxyTag}-{index}";
|
||||
|
||||
// Configure proxy chain relationships
|
||||
if (nextOutbound != null)
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
// If there's a next proxy, it should be the final outbound in the chain
|
||||
var originalTag = outbound.tag;
|
||||
outbound.tag = $"mid-{Global.ProxyTag}-{index}";
|
||||
|
||||
var nextOutboundCopy = JsonUtils.DeepCopy(nextOutbound);
|
||||
nextOutboundCopy.tag = originalTag;
|
||||
nextOutboundCopy.streamSettings.sockopt = new() { dialerProxy = outbound.tag };
|
||||
|
||||
if (prevTag != null)
|
||||
{
|
||||
outbound.streamSettings.sockopt = new() { dialerProxy = prevTag };
|
||||
}
|
||||
|
||||
// Add in reverse order to ensure final outbound is added first
|
||||
resultOutbounds.Add(nextOutboundCopy); // Final outbound (exposed to internet)
|
||||
resultOutbounds.Add(outbound); // Middle outbound
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no next proxy, the main outbound is the final one
|
||||
if (prevTag != null)
|
||||
{
|
||||
outbound.streamSettings.sockopt = new() { dialerProxy = prevTag };
|
||||
}
|
||||
else if (fragmentOutbound != null && outbound.streamSettings?.security.IsNullOrEmpty() == false)
|
||||
{
|
||||
outbound.streamSettings.sockopt = new() { dialerProxy = fragmentOutbound.tag };
|
||||
}
|
||||
|
||||
resultOutbounds.Add(outbound);
|
||||
resultOutbounds.Add(nextOutbound);
|
||||
}
|
||||
resultOutbounds.Add(currentOutbound);
|
||||
}
|
||||
|
||||
// Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds
|
||||
|
@ -1543,6 +1435,61 @@ public class CoreConfigV2rayService
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a chained outbound configuration for the given subItem and outbound.
|
||||
/// The outbound's tag must be set before calling this method.
|
||||
/// Returns the next proxy's outbound configuration, which may be null if no next proxy exists.
|
||||
/// </summary>
|
||||
/// <param name="subItem">The subscription item containing proxy chain information.</param>
|
||||
/// <param name="outbound">The current outbound configuration. Its tag must be set before calling this method.</param>
|
||||
/// <param name="prevOutboundTag">The tag of the previous outbound in the chain, if any.</param>
|
||||
/// <param name="nextOutbound">The outbound for the next proxy in the chain, if already created. If null, will be created inside.</param>
|
||||
/// <returns>
|
||||
/// The outbound configuration for the next proxy in the chain, or null if no next proxy exists.
|
||||
/// </returns>
|
||||
private async Task<Outbounds4Ray?> GenChainOutbounds(SubItem subItem, Outbounds4Ray outbound, string? prevOutboundTag, Outbounds4Ray? nextOutbound = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
|
||||
if (!prevOutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
outbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = prevOutboundTag
|
||||
};
|
||||
}
|
||||
|
||||
// Next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC)
|
||||
{
|
||||
if (nextOutbound == null)
|
||||
{
|
||||
nextOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
}
|
||||
nextOutbound.tag = outbound.tag;
|
||||
|
||||
outbound.tag = $"mid-{outbound.tag}";
|
||||
nextOutbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = outbound.tag
|
||||
};
|
||||
}
|
||||
return nextOutbound;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<int> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad)
|
||||
{
|
||||
if (multipleLoad == EMultipleLoad.LeastPing)
|
||||
|
|
|
@ -29,6 +29,9 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
[Reactive] public int hyUpMbps { get; set; }
|
||||
[Reactive] public int hyDownMbps { get; set; }
|
||||
[Reactive] public bool enableFragment { get; set; }
|
||||
[Reactive] public string fragmentPackets { get; set; }
|
||||
[Reactive] public string fragmentInterval { get; set; }
|
||||
[Reactive] public string fragmentLength { get; set; }
|
||||
|
||||
#endregion Core
|
||||
|
||||
|
@ -146,6 +149,9 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
hyUpMbps = _config.HysteriaItem.UpMbps;
|
||||
hyDownMbps = _config.HysteriaItem.DownMbps;
|
||||
enableFragment = _config.CoreBasicItem.EnableFragment;
|
||||
fragmentInterval = _config.Fragment4RayItem.Interval;
|
||||
fragmentLength = _config.Fragment4RayItem.Length;
|
||||
fragmentPackets = _config.Fragment4RayItem.Packets;
|
||||
|
||||
#endregion Core
|
||||
|
||||
|
@ -321,6 +327,9 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
_config.HysteriaItem.UpMbps = hyUpMbps;
|
||||
_config.HysteriaItem.DownMbps = hyDownMbps;
|
||||
_config.CoreBasicItem.EnableFragment = enableFragment;
|
||||
_config.Fragment4RayItem.Packets = fragmentPackets;
|
||||
_config.Fragment4RayItem.Interval = fragmentInterval;
|
||||
_config.Fragment4RayItem.Length = fragmentLength;
|
||||
|
||||
_config.GuiItem.AutoRun = AutoRun;
|
||||
_config.GuiItem.EnableStatistics = EnableStatistics;
|
||||
|
|
|
@ -66,6 +66,9 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
|
@ -399,6 +402,54 @@
|
|||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableFragmentTips}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="21"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsFragmentPackets}" />
|
||||
<TextBox
|
||||
x:Name="txtfragmentpackets"
|
||||
Grid.Row="21"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="22"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsFragmentLength}" />
|
||||
<TextBox
|
||||
x:Name="txtfragmentlength"
|
||||
Grid.Row="22"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="23"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsFragmentInterval}" />
|
||||
<TextBox
|
||||
x:Name="txtfragmentinterval"
|
||||
Grid.Row="23"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
|
|
@ -134,6 +134,9 @@ public partial class OptionSettingWindow
|
|||
this.Bind(ViewModel, vm => vm.hyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.hyDownMbps, v => v.txtDownMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.enableFragment, v => v.togenableFragment.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.fragmentInterval, v => v.txtfragmentinterval.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.fragmentLength, v => v.txtfragmentlength.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.fragmentPackets, v => v.txtfragmentpackets.Text).DisposeWith(disposables);
|
||||
|
||||
//this.Bind(ViewModel, vm => vm.Kcpmtu, v => v.txtKcpmtu.Text).DisposeWith(disposables);
|
||||
//this.Bind(ViewModel, vm => vm.Kcptti, v => v.txtKcptti.Text).DisposeWith(disposables);
|
||||
|
|
Loading…
Reference in a new issue