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 1b1b55c0a..34c452dfc 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 5e2727504..92950ba8c 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 e069869d5..a36d1a5bb 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 07dbddc18..12861bf60 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 2cb228151..2807b2897 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 a90bec7a8..0a782772c 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 a36d1a5bb..60d5cfa77 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 0a782772c..ac4ea20ad 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]));