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">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>فعال کردن فرگمنت</value>
|
<value>فعال کردن فرگمنت</value>
|
||||||
</data>
|
</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">
|
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||||
<value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value>
|
<value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1419,4 +1428,4 @@
|
||||||
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
||||||
<value>URL آزمایش اطلاعات اتصال فعلی</value>
|
<value>URL آزمایش اطلاعات اتصال فعلی</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -1131,6 +1131,15 @@
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>Fragmentum engedélyezése</value>
|
<value>Fragmentum engedélyezése</value>
|
||||||
</data>
|
</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">
|
<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>
|
<value>Cache fájl engedélyezése a sing-box számára (szabálykészlet fájlok)</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -1131,6 +1131,15 @@
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>Enable fragment</value>
|
<value>Enable fragment</value>
|
||||||
</data>
|
</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">
|
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||||
<value>Enable cache file for sing-box (ruleset files)</value>
|
<value>Enable cache file for sing-box (ruleset files)</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
<resheader name="version">2.0</resheader>
|
<resheader name="version">2.0</resheader>
|
||||||
|
@ -26,36 +26,36 @@
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
value : The object must be serialized into a byte array
|
value : The object must be serialized into a byte array
|
||||||
: using a System.ComponentModel.TypeConverter
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
|
@ -816,9 +816,6 @@
|
||||||
<data name="menuMoveUp" xml:space="preserve">
|
<data name="menuMoveUp" xml:space="preserve">
|
||||||
<value>Вверх (U)</value>
|
<value>Вверх (U)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuMoveTo" xml:space="preserve">
|
|
||||||
<value>Переместить вверх/вниз</value>
|
|
||||||
</data>
|
|
||||||
<data name="MsgFilterTitle" xml:space="preserve">
|
<data name="MsgFilterTitle" xml:space="preserve">
|
||||||
<value>Фильтр, поддерживает regex</value>
|
<value>Фильтр, поддерживает regex</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -990,6 +987,9 @@
|
||||||
<data name="TbSettingsSpeedTestUrl" xml:space="preserve">
|
<data name="TbSettingsSpeedTestUrl" xml:space="preserve">
|
||||||
<value>URL для тестирования скорости</value>
|
<value>URL для тестирования скорости</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuMoveTo" xml:space="preserve">
|
||||||
|
<value>Переместить вверх/вниз</value>
|
||||||
|
</data>
|
||||||
<data name="TbPublicKey" xml:space="preserve">
|
<data name="TbPublicKey" xml:space="preserve">
|
||||||
<value>PublicKey</value>
|
<value>PublicKey</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1131,6 +1131,15 @@
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>Включить фрагментацию (Fragment)</value>
|
<value>Включить фрагментацию (Fragment)</value>
|
||||||
</data>
|
</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">
|
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||||
<value>Включить файл кэша для sing-box (файлы наборов правил)</value>
|
<value>Включить файл кэша для sing-box (файлы наборов правил)</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1419,4 +1428,4 @@
|
||||||
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
||||||
<value>URL для тестирования текущего соединения</value>
|
<value>URL для тестирования текущего соединения</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -1128,6 +1128,15 @@
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>启用分片(Fragment)</value>
|
<value>启用分片(Fragment)</value>
|
||||||
</data>
|
</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">
|
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||||
<value>启用 sing-box(规则集文件)的缓存文件</value>
|
<value>启用 sing-box(规则集文件)的缓存文件</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -1128,6 +1128,15 @@
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>啟用分片(Fragment)</value>
|
<value>啟用分片(Fragment)</value>
|
||||||
</data>
|
</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">
|
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||||
<value>啟用 sing-box(規則集檔案)的快取檔案</value>
|
<value>啟用 sing-box(規則集檔案)的快取檔案</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -918,29 +918,21 @@ public class CoreConfigSingboxService
|
||||||
|
|
||||||
//Previous proxy
|
//Previous proxy
|
||||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||||
|
string? prevOutboundTag = null;
|
||||||
if (prevNode is not null
|
if (prevNode is not null
|
||||||
&& prevNode.ConfigType != EConfigType.Custom)
|
&& prevNode.ConfigType != EConfigType.Custom)
|
||||||
{
|
{
|
||||||
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||||
await GenOutbound(prevNode, prevOutbound);
|
await GenOutbound(prevNode, prevOutbound);
|
||||||
prevOutbound.tag = $"{Global.ProxyTag}2";
|
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
||||||
|
prevOutbound.tag = prevOutboundTag;
|
||||||
singboxConfig.outbounds.Add(prevOutbound);
|
singboxConfig.outbounds.Add(prevOutbound);
|
||||||
|
|
||||||
outbound.detour = prevOutbound.tag;
|
|
||||||
}
|
}
|
||||||
|
var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
|
||||||
|
|
||||||
//Next proxy
|
if (nextOutbound is not null)
|
||||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
|
||||||
if (nextNode is not null
|
|
||||||
&& nextNode.ConfigType != EConfigType.Custom)
|
|
||||||
{
|
{
|
||||||
var nextOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
|
||||||
await GenOutbound(nextNode, nextOutbound);
|
|
||||||
nextOutbound.tag = Global.ProxyTag;
|
|
||||||
singboxConfig.outbounds.Insert(0, nextOutbound);
|
singboxConfig.outbounds.Insert(0, nextOutbound);
|
||||||
|
|
||||||
outbound.tag = $"{Global.ProxyTag}1";
|
|
||||||
nextOutbound.detour = outbound.tag;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -967,8 +959,8 @@ public class CoreConfigSingboxService
|
||||||
var proxyTags = new List<string>(); // For selector and urltest outbounds
|
var proxyTags = new List<string>(); // For selector and urltest outbounds
|
||||||
|
|
||||||
// Cache for chain proxies to avoid duplicate generation
|
// Cache for chain proxies to avoid duplicate generation
|
||||||
var chainProxyCache = new Dictionary<string, (string?, Outbound4Sbox?)>();
|
var nextProxyCache = new Dictionary<string, Outbound4Sbox?>();
|
||||||
var prevProxyTags = new Dictionary<string, string>(); // Map from profile name to tag
|
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
||||||
int prevIndex = 0; // Index for prev outbounds
|
int prevIndex = 0; // Index for prev outbounds
|
||||||
|
|
||||||
// Process each node
|
// Process each node
|
||||||
|
@ -977,112 +969,55 @@ public class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
// Skip unsupported config types
|
|
||||||
if (node.ConfigType is EConfigType.Custom)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle proxy chain
|
// Handle proxy chain
|
||||||
string? prevTag = null;
|
string? prevTag = null;
|
||||||
Outbound4Sbox? nextOutbound = null;
|
var currentOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||||
|
var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null);
|
||||||
if (node.Subid.IsNotEmpty())
|
if (nextOutbound != null)
|
||||||
{
|
{
|
||||||
// Check if chain proxy is already cached
|
nextOutbound = JsonUtils.DeepCopy(nextOutbound);
|
||||||
if (chainProxyCache.TryGetValue(node.Subid, out var chainProxy))
|
}
|
||||||
|
|
||||||
|
var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
|
||||||
|
|
||||||
|
// current proxy
|
||||||
|
await GenOutbound(node, currentOutbound);
|
||||||
|
currentOutbound.tag = $"{Global.ProxyTag}-{index}";
|
||||||
|
proxyTags.Add(currentOutbound.tag);
|
||||||
|
|
||||||
|
if (!node.Subid.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
if (prevProxyTags.TryGetValue(node.Subid, out var value))
|
||||||
{
|
{
|
||||||
prevTag = chainProxy.Item1;
|
prevTag = value; // maybe null
|
||||||
nextOutbound = chainProxy.Item2;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Generate chain proxy and cache it
|
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||||
var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
|
if (prevNode is not null
|
||||||
if (subItem != null)
|
&& prevNode.ConfigType != EConfigType.Custom)
|
||||||
{
|
{
|
||||||
// Process previous proxy
|
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||||
if (!subItem.PrevProfile.IsNullOrEmpty())
|
await GenOutbound(prevNode, prevOutbound);
|
||||||
{
|
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
||||||
// Check if this previous proxy was already created
|
prevOutbound.tag = prevTag;
|
||||||
if (prevProxyTags.TryGetValue(subItem.PrevProfile, out var existingTag))
|
prevOutbounds.Add(prevOutbound);
|
||||||
{
|
|
||||||
prevTag = existingTag;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
|
||||||
if (prevNode != null && prevNode.ConfigType != EConfigType.Custom)
|
|
||||||
{
|
|
||||||
prevIndex++;
|
|
||||||
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
|
||||||
await GenOutbound(prevNode, prevOutbound);
|
|
||||||
|
|
||||||
prevTag = $"{Global.ProxyTag}-prev-{prevIndex}";
|
|
||||||
prevOutbound.tag = prevTag;
|
|
||||||
prevProxyTags[subItem.PrevProfile] = prevTag;
|
|
||||||
|
|
||||||
// Add to prev outbounds list (will be added at the end)
|
|
||||||
prevOutbounds.Add(prevOutbound);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process next proxy
|
|
||||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
|
||||||
if (nextNode != null && nextNode.ConfigType != EConfigType.Custom)
|
|
||||||
{
|
|
||||||
nextOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
|
||||||
await GenOutbound(nextNode, nextOutbound);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache the chain proxy
|
|
||||||
chainProxyCache[node.Subid] = (prevTag, nextOutbound);
|
|
||||||
}
|
}
|
||||||
|
prevProxyTags[node.Subid] = prevTag;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Create main outbound
|
nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound);
|
||||||
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
if (!nextProxyCache.ContainsKey(node.Subid))
|
||||||
|
|
||||||
await GenOutbound(node, outbound);
|
|
||||||
outbound.tag = $"{Global.ProxyTag}-{index}";
|
|
||||||
|
|
||||||
// Configure proxy chain relationships
|
|
||||||
if (nextOutbound != 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;
|
nextProxyCache[node.Subid] = nextOutbound;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (nextOutbound is not null)
|
||||||
{
|
{
|
||||||
// If no next proxy, the main outbound is the final one
|
resultOutbounds.Add(nextOutbound);
|
||||||
if (prevTag != null)
|
|
||||||
{
|
|
||||||
outbound.detour = prevTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to proxy tags for selector/urltest
|
|
||||||
proxyTags.Add(outbound.tag);
|
|
||||||
resultOutbounds.Add(outbound);
|
|
||||||
}
|
}
|
||||||
|
resultOutbounds.Add(currentOutbound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add urltest outbound (auto selection based on latency)
|
// Add urltest outbound (auto selection based on latency)
|
||||||
|
@ -1124,6 +1059,53 @@ public class CoreConfigSingboxService
|
||||||
return 0;
|
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)
|
private async Task<int> GenRouting(SingboxConfig singboxConfig)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -1318,6 +1318,7 @@ public class CoreConfigV2rayService
|
||||||
|
|
||||||
//Previous proxy
|
//Previous proxy
|
||||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||||
|
string? prevOutboundTag = null;
|
||||||
if (prevNode is not null
|
if (prevNode is not null
|
||||||
&& prevNode.ConfigType != EConfigType.Custom
|
&& prevNode.ConfigType != EConfigType.Custom
|
||||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||||
|
@ -1325,32 +1326,15 @@ public class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||||
await GenOutbound(prevNode, prevOutbound);
|
await GenOutbound(prevNode, prevOutbound);
|
||||||
prevOutbound.tag = $"{Global.ProxyTag}2";
|
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
||||||
|
prevOutbound.tag = prevOutboundTag;
|
||||||
v2rayConfig.outbounds.Add(prevOutbound);
|
v2rayConfig.outbounds.Add(prevOutbound);
|
||||||
|
|
||||||
outbound.streamSettings.sockopt = new()
|
|
||||||
{
|
|
||||||
dialerProxy = prevOutbound.tag
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
|
||||||
|
|
||||||
//Next proxy
|
if (nextOutbound is not null)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
var nextOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
|
||||||
await GenOutbound(nextNode, nextOutbound);
|
|
||||||
nextOutbound.tag = Global.ProxyTag;
|
|
||||||
v2rayConfig.outbounds.Insert(0, nextOutbound);
|
v2rayConfig.outbounds.Insert(0, nextOutbound);
|
||||||
|
|
||||||
outbound.tag = $"{Global.ProxyTag}1";
|
|
||||||
nextOutbound.streamSettings.sockopt = new()
|
|
||||||
{
|
|
||||||
dialerProxy = outbound.tag
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -1375,31 +1359,9 @@ public class CoreConfigV2rayService
|
||||||
var resultOutbounds = new List<Outbounds4Ray>();
|
var resultOutbounds = new List<Outbounds4Ray>();
|
||||||
var prevOutbounds = new List<Outbounds4Ray>(); // Separate list for prev outbounds and fragment
|
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
|
// Cache for chain proxies to avoid duplicate generation
|
||||||
var chainProxyCache = new Dictionary<string, (string?, Outbounds4Ray?)>();
|
var nextProxyCache = new Dictionary<string, Outbounds4Ray?>();
|
||||||
var prevProxyTags = new Dictionary<string, string>(); // Map from profile name to tag
|
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
||||||
int prevIndex = 0; // Index for prev outbounds
|
int prevIndex = 0; // Index for prev outbounds
|
||||||
|
|
||||||
// Process nodes
|
// Process nodes
|
||||||
|
@ -1408,126 +1370,56 @@ public class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
// Skip unsupported config types
|
|
||||||
if (node.ConfigType is EConfigType.Custom or EConfigType.Hysteria2 or EConfigType.TUIC)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle proxy chain
|
// Handle proxy chain
|
||||||
string? prevTag = null;
|
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())
|
if (!node.Subid.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
// Check if chain proxy is already cached
|
if (prevProxyTags.TryGetValue(node.Subid, out var value))
|
||||||
if (chainProxyCache.TryGetValue(node.Subid, out var chainProxy))
|
|
||||||
{
|
{
|
||||||
prevTag = chainProxy.Item1;
|
prevTag = value; // maybe null
|
||||||
nextOutbound = chainProxy.Item2;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Generate chain proxy and cache it
|
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||||
var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
|
if (prevNode is not null
|
||||||
if (subItem != null)
|
&& prevNode.ConfigType != EConfigType.Custom
|
||||||
|
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||||
|
&& prevNode.ConfigType != EConfigType.TUIC)
|
||||||
{
|
{
|
||||||
// Process previous proxy
|
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||||
if (!subItem.PrevProfile.IsNullOrEmpty())
|
await GenOutbound(prevNode, prevOutbound);
|
||||||
{
|
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
||||||
// Check if this previous proxy was already created
|
prevOutbound.tag = prevTag;
|
||||||
if (prevProxyTags.TryGetValue(subItem.PrevProfile, out var existingTag))
|
prevOutbounds.Add(prevOutbound);
|
||||||
{
|
|
||||||
prevTag = existingTag;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
|
||||||
if (prevNode != 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}";
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
|
||||||
await GenOutbound(nextNode, nextOutbound);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache the chain proxy
|
|
||||||
chainProxyCache[node.Subid] = (prevTag, nextOutbound);
|
|
||||||
}
|
}
|
||||||
|
prevProxyTags[node.Subid] = prevTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound);
|
||||||
|
if (!nextProxyCache.ContainsKey(node.Subid))
|
||||||
|
{
|
||||||
|
nextProxyCache[node.Subid] = nextOutbound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create main outbound
|
if (nextOutbound is not null)
|
||||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
|
||||||
|
|
||||||
await GenOutbound(node, outbound);
|
|
||||||
outbound.tag = $"{Global.ProxyTag}-{index}";
|
|
||||||
|
|
||||||
// Configure proxy chain relationships
|
|
||||||
if (nextOutbound != null)
|
|
||||||
{
|
{
|
||||||
// If there's a next proxy, it should be the final outbound in the chain
|
resultOutbounds.Add(nextOutbound);
|
||||||
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(currentOutbound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds
|
// Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds
|
||||||
|
@ -1543,6 +1435,61 @@ public class CoreConfigV2rayService
|
||||||
return 0;
|
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)
|
private async Task<int> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad)
|
||||||
{
|
{
|
||||||
if (multipleLoad == EMultipleLoad.LeastPing)
|
if (multipleLoad == EMultipleLoad.LeastPing)
|
||||||
|
|
|
@ -29,6 +29,9 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||||
[Reactive] public int hyUpMbps { get; set; }
|
[Reactive] public int hyUpMbps { get; set; }
|
||||||
[Reactive] public int hyDownMbps { get; set; }
|
[Reactive] public int hyDownMbps { get; set; }
|
||||||
[Reactive] public bool enableFragment { 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
|
#endregion Core
|
||||||
|
|
||||||
|
@ -146,6 +149,9 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||||
hyUpMbps = _config.HysteriaItem.UpMbps;
|
hyUpMbps = _config.HysteriaItem.UpMbps;
|
||||||
hyDownMbps = _config.HysteriaItem.DownMbps;
|
hyDownMbps = _config.HysteriaItem.DownMbps;
|
||||||
enableFragment = _config.CoreBasicItem.EnableFragment;
|
enableFragment = _config.CoreBasicItem.EnableFragment;
|
||||||
|
fragmentInterval = _config.Fragment4RayItem.Interval;
|
||||||
|
fragmentLength = _config.Fragment4RayItem.Length;
|
||||||
|
fragmentPackets = _config.Fragment4RayItem.Packets;
|
||||||
|
|
||||||
#endregion Core
|
#endregion Core
|
||||||
|
|
||||||
|
@ -321,6 +327,9 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||||
_config.HysteriaItem.UpMbps = hyUpMbps;
|
_config.HysteriaItem.UpMbps = hyUpMbps;
|
||||||
_config.HysteriaItem.DownMbps = hyDownMbps;
|
_config.HysteriaItem.DownMbps = hyDownMbps;
|
||||||
_config.CoreBasicItem.EnableFragment = enableFragment;
|
_config.CoreBasicItem.EnableFragment = enableFragment;
|
||||||
|
_config.Fragment4RayItem.Packets = fragmentPackets;
|
||||||
|
_config.Fragment4RayItem.Interval = fragmentInterval;
|
||||||
|
_config.Fragment4RayItem.Length = fragmentLength;
|
||||||
|
|
||||||
_config.GuiItem.AutoRun = AutoRun;
|
_config.GuiItem.AutoRun = AutoRun;
|
||||||
_config.GuiItem.EnableStatistics = EnableStatistics;
|
_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" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
@ -399,6 +402,54 @@
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsEnableFragmentTips}"
|
Text="{x:Static resx:ResUI.TbSettingsEnableFragmentTips}"
|
||||||
TextWrapping="Wrap" />
|
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>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</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.hyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.hyDownMbps, v => v.txtDownMbps.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.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.Kcpmtu, v => v.txtKcpmtu.Text).DisposeWith(disposables);
|
||||||
//this.Bind(ViewModel, vm => vm.Kcptti, v => v.txtKcptti.Text).DisposeWith(disposables);
|
//this.Bind(ViewModel, vm => vm.Kcptti, v => v.txtKcptti.Text).DisposeWith(disposables);
|
||||||
|
|
Loading…
Reference in a new issue