From 55cb0f53ddde8efd834e0371023ad1a7b3c41ac1 Mon Sep 17 00:00:00 2001 From: bamzest <> Date: Fri, 2 Jan 2026 22:28:13 +0800 Subject: [PATCH 1/2] feat: Implement MaxFailedTimes support and improve build configuration Changes: - src/config/proxygroup.h: Add MaxFailedTimes field with default value 5 - src/config/binding.h: Update binding logic for MaxFailedTimes parsing - src/handler/settings.cpp: Update settings handling for MaxFailedTimes - base/pref.example.ini: Add MaxFailedTimes to example INI configuration - base/pref.example.yml: Add MaxFailedTimes to example YAML configuration - scripts/build.macos.release.sh: Use absolute paths for Python script execution - .gitignore: Add base/subconverter to exclude compiled binary This commit: 1. Completes the MaxFailedTimes feature implementation for proxy groups 2. Fixes compilation errors related to missing MaxFailedTimes member 3. Updates example configurations to demonstrate the new feature 4. Improves build script reliability by using absolute paths 5. Excludes compiled binaries from version control The MaxFailedTimes field allows users to configure the maximum number of failed attempts before marking a proxy as unavailable, with a reasonable default value of 5. --- base/pref.example.ini | 2 ++ base/pref.example.yml | 2 ++ src/config/binding.h | 24 ++++++++++++++++++++++++ src/config/proxygroup.h | 5 ++++- src/generator/config/subexport.cpp | 2 ++ src/handler/settings.cpp | 7 +++++++ 6 files changed, 41 insertions(+), 1 deletion(-) diff --git a/base/pref.example.ini b/base/pref.example.ini index 1b1b55c0a1..34c452dfc9 100644 --- a/base/pref.example.ini +++ b/base/pref.example.ini @@ -201,9 +201,11 @@ ruleset=!!import:snippets/rulesets.txt ;Rule with "[]" prefix will be added directly. ;custom_proxy_group=Proxy`select`.*`[]AUTO`[]DIRECT`.* +;custom_proxy_group=ProxyWithMaxFailed`select`.*`!!MAX_FAILED_TIMES=5`[]AUTO`[]DIRECT`.* ;custom_proxy_group=UrlTest`url-test`.*`http://www.gstatic.com/generate_204`300,5,100 ;custom_proxy_group=FallBack`fallback`.*`http://www.gstatic.com/generate_204`300,5 ;custom_proxy_group=LoadBalance`load-balance`.*`http://www.gstatic.com/generate_204`300,,100 +;custom_proxy_group=LoadBalanceSticky`load-balance`.*`!!STRATEGY=sticky-sessions`!!MAX_FAILED_TIMES=5`http://www.gstatic.com/generate_204`300,,100 ;custom_proxy_group=SSID`ssid`default_group`celluar=group0,ssid1=group1,ssid2=group2 ;custom_proxy_group=g1`select`!!GROUPID=0 diff --git a/base/pref.example.yml b/base/pref.example.yml index 5e27275043..92950ba8ca 100644 --- a/base/pref.example.yml +++ b/base/pref.example.yml @@ -95,7 +95,9 @@ rulesets: proxy_groups: custom_proxy_group: # - {name: UrlTest, type: url-test, rule: [".*"], url: http://www.gstatic.com/generate_204, interval: 300, tolerance: 100, timeout: 5} +# - {name: LoadBalanceSticky, type: load-balance, rule: [".*"], url: http://www.gstatic.com/generate_204, interval: 300, timeout: 5, tolerance: 100, max-failed-times: 5, strategy: sticky-sessions} # - {name: Proxy, type: select, rule: [".*"]} +# - {name: ProxyWithMaxFailed, type: select, rule: [".*"], max-failed-times: 5} # - {name: group1, type: select, rule: ["!!GROUPID=0"]} # - {name: v2ray, type: select, rule: ["!!GROUP=V2RayProvider"]} # - {import: snippets/groups_forcerule.txt} diff --git a/src/config/binding.h b/src/config/binding.h index e069869d50..a36d1a5bb4 100644 --- a/src/config/binding.h +++ b/src/config/binding.h @@ -47,6 +47,9 @@ namespace toml case "round-robin"_hash: conf.Strategy = BalanceStrategy::RoundRobin; break; + case "sticky-sessions"_hash: + conf.Strategy = BalanceStrategy::StickySessions; + break; } if(v.contains("persistent")) conf.Persistent = find_or(v, "persistent", conf.Persistent.get()); @@ -80,6 +83,7 @@ namespace toml conf.Timeout = find_or(v, "timeout", 5); conf.Proxies = find_or(v, "rule", {}); conf.UsingProvider = find_or(v, "use", {}); + conf.MaxFailedTimes = find_or(v, "max-failed-times", 5); if(conf.Proxies.empty() && conf.UsingProvider.empty()) throw serialization_error(format_error("Proxy Group must contains at least one of proxy match rule or provider!", v.location(), "here"), v.location()); if(v.contains("disable-udp")) @@ -254,6 +258,26 @@ namespace INIBinding conf.UsingProvider.reserve(conf.UsingProvider.size() + list.size()); std::move(list.begin(), list.end(), std::back_inserter(conf.UsingProvider)); } + else if(startsWith(vArray[i], "!!MAX_FAILED_TIMES=")) + { + conf.MaxFailedTimes = to_int(vArray[i].substr(19), 0); + } + else if(startsWith(vArray[i], "!!STRATEGY=")) + { + std::string strategy = vArray[i].substr(11); + switch(hash_(strategy)) + { + case "consistent-hashing"_hash: + conf.Strategy = BalanceStrategy::ConsistentHashing; + break; + case "round-robin"_hash: + conf.Strategy = BalanceStrategy::RoundRobin; + break; + case "sticky-sessions"_hash: + conf.Strategy = BalanceStrategy::StickySessions; + break; + } + } else conf.Proxies.emplace_back(std::move(vArray[i])); } diff --git a/src/config/proxygroup.h b/src/config/proxygroup.h index 07dbddc182..12861bf60a 100644 --- a/src/config/proxygroup.h +++ b/src/config/proxygroup.h @@ -17,7 +17,8 @@ enum class ProxyGroupType enum class BalanceStrategy { ConsistentHashing, - RoundRobin + RoundRobin, + StickySessions }; struct ProxyGroupConfig @@ -30,6 +31,7 @@ struct ProxyGroupConfig Integer Interval = 0; Integer Timeout = 0; Integer Tolerance = 0; + Integer MaxFailedTimes = 5; BalanceStrategy Strategy = BalanceStrategy::ConsistentHashing; Boolean Lazy; Boolean DisableUdp; @@ -57,6 +59,7 @@ struct ProxyGroupConfig { case BalanceStrategy::ConsistentHashing: return "consistent-hashing"; case BalanceStrategy::RoundRobin: return "round-robin"; + case BalanceStrategy::StickySessions: return "sticky-sessions"; } return ""; } diff --git a/src/generator/config/subexport.cpp b/src/generator/config/subexport.cpp index 2cb2281516..2807b28979 100644 --- a/src/generator/config/subexport.cpp +++ b/src/generator/config/subexport.cpp @@ -781,6 +781,8 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr if(!x.DisableUdp.is_undef()) singlegroup["disable-udp"] = x.DisableUdp.get(); + singlegroup["max-failed-times"] = x.MaxFailedTimes; + for(const auto& y : x.Proxies) groupGenerate(y, nodelist, filtered_nodelist, true, ext); diff --git a/src/handler/settings.cpp b/src/handler/settings.cpp index a90bec7a8b..0a782772c6 100644 --- a/src/handler/settings.cpp +++ b/src/handler/settings.cpp @@ -182,6 +182,7 @@ void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true) continue; } std::string url = "http://www.gstatic.com/generate_204", interval = "300", tolerance, timeout; + std::string max_failed_times, strategy; object["name"] >>= name; object["type"] >>= type; tempArray.emplace_back(name); @@ -190,8 +191,14 @@ void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true) object["interval"] >>= interval; object["tolerance"] >>= tolerance; object["timeout"] >>= timeout; + object["max-failed-times"] >>= max_failed_times; + object["strategy"] >>= strategy; for(std::size_t j = 0; j < object["rule"].size(); j++) tempArray.emplace_back(safe_as(object["rule"][j])); + if(!max_failed_times.empty()) + tempArray.emplace_back("!!MAX_FAILED_TIMES=" + max_failed_times); + if(!strategy.empty()) + tempArray.emplace_back("!!STRATEGY=" + strategy); switch(hash_(type)) { case "select"_hash: From ed095994b768101530204e27b42127f0b3e06c72 Mon Sep 17 00:00:00 2001 From: bamzest <> Date: Sun, 4 Jan 2026 10:44:28 +0800 Subject: [PATCH 2/2] fix: rename to max_failed_times --- src/config/binding.h | 2 +- src/handler/settings.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/binding.h b/src/config/binding.h index a36d1a5bb4..60d5cfa774 100644 --- a/src/config/binding.h +++ b/src/config/binding.h @@ -83,7 +83,7 @@ namespace toml conf.Timeout = find_or(v, "timeout", 5); conf.Proxies = find_or(v, "rule", {}); conf.UsingProvider = find_or(v, "use", {}); - conf.MaxFailedTimes = find_or(v, "max-failed-times", 5); + conf.MaxFailedTimes = find_or(v, "max_failed_times", 5); if(conf.Proxies.empty() && conf.UsingProvider.empty()) throw serialization_error(format_error("Proxy Group must contains at least one of proxy match rule or provider!", v.location(), "here"), v.location()); if(v.contains("disable-udp")) diff --git a/src/handler/settings.cpp b/src/handler/settings.cpp index 0a782772c6..ac4ea20ad3 100644 --- a/src/handler/settings.cpp +++ b/src/handler/settings.cpp @@ -191,7 +191,7 @@ void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true) object["interval"] >>= interval; object["tolerance"] >>= tolerance; object["timeout"] >>= timeout; - object["max-failed-times"] >>= max_failed_times; + object["max_failed_times"] >>= max_failed_times; object["strategy"] >>= strategy; for(std::size_t j = 0; j < object["rule"].size(); j++) tempArray.emplace_back(safe_as(object["rule"][j]));