@@ -336,7 +336,7 @@ public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem>
336
336
await GenExperimental ( singboxConfig ) ;
337
337
singboxConfig . outbounds . RemoveAt ( 0 ) ;
338
338
339
- var tagProxy = new List < string > ( ) ;
339
+ var proxyProfiles = new List < ProfileItem > ( ) ;
340
340
foreach ( var it in selecteds )
341
341
{
342
342
if ( it . ConfigType == EConfigType . Custom )
@@ -370,42 +370,18 @@ public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem>
370
370
}
371
371
372
372
//outbound
373
- var outbound = JsonUtils . Deserialize < Outbound4Sbox > ( txtOutbound ) ;
374
- await GenOutbound ( item , outbound ) ;
375
- outbound . tag = $ "{ Global . ProxyTag } -{ tagProxy . Count + 1 } ";
376
- singboxConfig . outbounds . Insert ( 0 , outbound ) ;
377
- tagProxy . Add ( outbound . tag ) ;
373
+ proxyProfiles . Add ( item ) ;
378
374
}
379
- if ( tagProxy . Count <= 0 )
375
+ if ( proxyProfiles . Count <= 0 )
380
376
{
381
377
ret . Msg = ResUI . FailedGenDefaultConfiguration ;
382
378
return ret ;
383
379
}
380
+ await GenOutboundsList ( proxyProfiles , singboxConfig ) ;
384
381
385
382
await GenDns ( null , singboxConfig ) ;
386
383
await ConvertGeo2Ruleset ( singboxConfig ) ;
387
384
388
- //add urltest outbound
389
- var outUrltest = new Outbound4Sbox
390
- {
391
- type = "urltest" ,
392
- tag = $ "{ Global . ProxyTag } -auto",
393
- outbounds = tagProxy ,
394
- interrupt_exist_connections = false ,
395
- } ;
396
- singboxConfig . outbounds . Insert ( 0 , outUrltest ) ;
397
-
398
- //add selector outbound
399
- var outSelector = new Outbound4Sbox
400
- {
401
- type = "selector" ,
402
- tag = Global . ProxyTag ,
403
- outbounds = JsonUtils . DeepCopy ( tagProxy ) ,
404
- interrupt_exist_connections = false ,
405
- } ;
406
- outSelector . outbounds . Insert ( 0 , outUrltest . tag ) ;
407
- singboxConfig . outbounds . Insert ( 0 , outSelector ) ;
408
-
409
385
ret . Success = true ;
410
386
ret . Data = JsonUtils . Serialize ( singboxConfig ) ;
411
387
return ret ;
@@ -974,6 +950,179 @@ private async Task<int> GenMoreOutbounds(ProfileItem node, SingboxConfig singbox
974
950
return 0 ;
975
951
}
976
952
953
+ private async Task < int > GenOutboundsList ( List < ProfileItem > nodes , SingboxConfig singboxConfig )
954
+ {
955
+ try
956
+ {
957
+ // Get outbound template and initialize lists
958
+ var txtOutbound = EmbedUtils . GetEmbedText ( Global . SingboxSampleOutbound ) ;
959
+ if ( txtOutbound . IsNullOrEmpty ( ) )
960
+ {
961
+ return 0 ;
962
+ }
963
+
964
+ var resultOutbounds = new List < Outbound4Sbox > ( ) ;
965
+ var prevOutbounds = new List < Outbound4Sbox > ( ) ; // Separate list for prev outbounds
966
+ var proxyTags = new List < string > ( ) ; // For selector and urltest outbounds
967
+
968
+ // Cache for chain proxies to avoid duplicate generation
969
+ var chainProxyCache = new Dictionary < string , ( string ? , Outbound4Sbox ? ) > ( ) ;
970
+ var prevProxyTags = new Dictionary < string , string > ( ) ; // Map from profile name to tag
971
+ int prevIndex = 0 ; // Index for prev outbounds
972
+
973
+ // Process each node
974
+ int index = 0 ;
975
+ foreach ( var node in nodes )
976
+ {
977
+ index ++ ;
978
+
979
+ // Skip unsupported config types
980
+ if ( node . ConfigType is EConfigType . Custom )
981
+ {
982
+ continue ;
983
+ }
984
+
985
+ // Handle proxy chain
986
+ string ? prevTag = null ;
987
+ Outbound4Sbox ? nextOutbound = null ;
988
+
989
+ if ( node . Subid . IsNotEmpty ( ) )
990
+ {
991
+ // Check if chain proxy is already cached
992
+ if ( chainProxyCache . TryGetValue ( node . Subid , out var chainProxy ) )
993
+ {
994
+ prevTag = chainProxy . Item1 ;
995
+ nextOutbound = chainProxy . Item2 ;
996
+ }
997
+ else
998
+ {
999
+ // Generate chain proxy and cache it
1000
+ var subItem = await AppHandler . Instance . GetSubItem ( node . Subid ) ;
1001
+ if ( subItem != null )
1002
+ {
1003
+ // Process previous proxy
1004
+ if ( ! subItem . PrevProfile . IsNullOrEmpty ( ) )
1005
+ {
1006
+ // Check if this previous proxy was already created
1007
+ if ( prevProxyTags . TryGetValue ( subItem . PrevProfile , out var existingTag ) )
1008
+ {
1009
+ prevTag = existingTag ;
1010
+ }
1011
+ else
1012
+ {
1013
+ var prevNode = await AppHandler . Instance . GetProfileItemViaRemarks ( subItem . PrevProfile ) ;
1014
+ if ( prevNode != null && prevNode . ConfigType != EConfigType . Custom )
1015
+ {
1016
+ prevIndex ++ ;
1017
+ var prevOutbound = JsonUtils . Deserialize < Outbound4Sbox > ( txtOutbound ) ;
1018
+ await GenOutbound ( prevNode , prevOutbound ) ;
1019
+
1020
+ prevTag = $ "{ Global . ProxyTag } -prev-{ prevIndex } ";
1021
+ prevOutbound . tag = prevTag ;
1022
+ prevProxyTags [ subItem . PrevProfile ] = prevTag ;
1023
+
1024
+ // Add to prev outbounds list (will be added at the end)
1025
+ prevOutbounds . Add ( prevOutbound ) ;
1026
+ }
1027
+ }
1028
+ }
1029
+
1030
+ // Process next proxy
1031
+ var nextNode = await AppHandler . Instance . GetProfileItemViaRemarks ( subItem . NextProfile ) ;
1032
+ if ( nextNode != null && nextNode . ConfigType != EConfigType . Custom )
1033
+ {
1034
+ nextOutbound = JsonUtils . Deserialize < Outbound4Sbox > ( txtOutbound ) ;
1035
+ await GenOutbound ( nextNode , nextOutbound ) ;
1036
+ }
1037
+
1038
+ // Cache the chain proxy
1039
+ chainProxyCache [ node . Subid ] = ( prevTag , nextOutbound ) ;
1040
+ }
1041
+ }
1042
+ }
1043
+
1044
+ // Create main outbound
1045
+ var outbound = JsonUtils . Deserialize < Outbound4Sbox > ( txtOutbound ) ;
1046
+
1047
+ await GenOutbound ( node , outbound ) ;
1048
+ outbound . tag = $ "{ Global . ProxyTag } -{ index } ";
1049
+
1050
+ // Configure proxy chain relationships
1051
+ if ( nextOutbound != null )
1052
+ {
1053
+ // If there's a next proxy, it should be the final outbound in the chain
1054
+ var originalTag = outbound . tag ;
1055
+ outbound . tag = $ "mid-{ Global . ProxyTag } -{ index } ";
1056
+
1057
+ var nextOutboundCopy = JsonUtils . DeepCopy ( nextOutbound ) ;
1058
+ nextOutboundCopy . tag = originalTag ;
1059
+ nextOutboundCopy . detour = outbound . tag ; // Use detour instead of sockopt
1060
+
1061
+ if ( prevTag != null )
1062
+ {
1063
+ outbound . detour = prevTag ;
1064
+ }
1065
+
1066
+ // Add to proxy tags for selector/urltest
1067
+ proxyTags . Add ( originalTag ) ;
1068
+
1069
+ // Add in reverse order to ensure final outbound is added first
1070
+ resultOutbounds . Add ( nextOutboundCopy ) ; // Final outbound (exposed to internet)
1071
+ resultOutbounds . Add ( outbound ) ; // Middle outbound
1072
+ }
1073
+ else
1074
+ {
1075
+ // If no next proxy, the main outbound is the final one
1076
+ if ( prevTag != null )
1077
+ {
1078
+ outbound . detour = prevTag ;
1079
+ }
1080
+
1081
+ // Add to proxy tags for selector/urltest
1082
+ proxyTags . Add ( outbound . tag ) ;
1083
+ resultOutbounds . Add ( outbound ) ;
1084
+ }
1085
+ }
1086
+
1087
+ // Add urltest outbound (auto selection based on latency)
1088
+ if ( proxyTags . Count > 0 )
1089
+ {
1090
+ var outUrltest = new Outbound4Sbox
1091
+ {
1092
+ type = "urltest" ,
1093
+ tag = $ "{ Global . ProxyTag } -auto",
1094
+ outbounds = proxyTags ,
1095
+ interrupt_exist_connections = false ,
1096
+ } ;
1097
+
1098
+ // Add selector outbound (manual selection)
1099
+ var outSelector = new Outbound4Sbox
1100
+ {
1101
+ type = "selector" ,
1102
+ tag = Global . ProxyTag ,
1103
+ outbounds = JsonUtils . DeepCopy ( proxyTags ) ,
1104
+ interrupt_exist_connections = false ,
1105
+ } ;
1106
+ outSelector . outbounds . Insert ( 0 , outUrltest . tag ) ;
1107
+
1108
+ // Insert these at the beginning
1109
+ resultOutbounds . Insert ( 0 , outUrltest ) ;
1110
+ resultOutbounds . Insert ( 0 , outSelector ) ;
1111
+ }
1112
+
1113
+ // Merge results: first the selector/urltest/proxies, then other outbounds, and finally prev outbounds
1114
+ resultOutbounds . AddRange ( prevOutbounds ) ;
1115
+ resultOutbounds . AddRange ( singboxConfig . outbounds ) ;
1116
+ singboxConfig . outbounds = resultOutbounds ;
1117
+ }
1118
+ catch ( Exception ex )
1119
+ {
1120
+ Logging . SaveLog ( _tag , ex ) ;
1121
+ }
1122
+
1123
+ return 0 ;
1124
+ }
1125
+
977
1126
private async Task < int > GenRouting ( SingboxConfig singboxConfig )
978
1127
{
979
1128
try
0 commit comments