feat: debug
Some checks failed
Build Android APK / 编译 libcore.aar (push) Has been cancelled
Build Android APK / 编译 Android APK (release) (push) Has been cancelled
Build Android APK / 创建 GitHub Release (push) Has been cancelled
Build Multi-Platform / 编译 libcore (iOS/tvOS) (push) Has been cancelled
Build Windows / 编译 libcore (Windows) (push) Has been cancelled
Build Windows / build (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Android) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Windows) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (macOS) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Linux) (push) Has been cancelled
Build Multi-Platform / 构建 Android APK (push) Has been cancelled
Build Multi-Platform / 构建 Windows (push) Has been cancelled
Build Multi-Platform / 构建 macOS (push) Has been cancelled
Build Multi-Platform / 构建 Linux (push) Has been cancelled
Build Multi-Platform / 构建 iOS (push) Has been cancelled
Build Multi-Platform / 创建 Release (push) Has been cancelled

This commit is contained in:
speakeloudest 2025-11-01 23:14:39 -07:00
parent 1e7175edf5
commit 765b45c0fc
4 changed files with 185 additions and 282 deletions

View File

@ -25,67 +25,20 @@ class HINodeListController extends GetxController {
///
void kr_updateConnectionType(KRConnectionType type) {
if (KRSingBoxImp().kr_connectionType.value != type) {
KRSingBoxImp.instance.kr_updateConnectionType(type);
KRLogUtil.kr_i('连接类型已更新为: $type', tag: 'HINodeListController');
//
}
//
if (homeController.currentSelectedCountry.isNotEmpty) {
KRLogUtil.kr_i('连接类型更新后,检查是否需要重选节点', tag: 'HINodeListController');
//
Future.delayed(const Duration(milliseconds: 500), () {
homeController.checkCountryReselection(KRSingBoxImp.instance.kr_activeGroups);
// homeController.checkCountryReselection(KRSingBoxImp.instance.kr_activeGroups);
});
}
}
///
List<String> getValidNodesInCurrentCountry() {
final country = homeController.currentSelectedCountry.value;
if (country.isEmpty) {
return [];
}
final countryGroup = kr_subscribeService.countryOutboundList
.firstWhereOrNull((group) => group.country == country);
if (countryGroup == null) {
return [];
}
return countryGroup.outboundList
.where((node) => node.urlTestDelay.value < 65535 && node.urlTestDelay.value > 0)
.map((node) => node.tag)
.toList();
}
///
void manualReselection() {
final country = homeController.currentSelectedCountry.value;
if (country.isEmpty) {
KRLogUtil.kr_w('没有选择国家,无法进行重选', tag: 'HINodeListController');
return;
}
KRLogUtil.kr_i('用户手动触发国家内重选', tag: 'HINodeListController');
homeController.performCountryReselection(country);
}
/// homeController
Map<String, dynamic> getCurrentCountryLatencyStats() {
return homeController.getCurrentCountryNodeStats();
}
/// /homeController
void setDynamicReselectionEnabled(bool enabled) {
homeController.setCountryReselectionEnabled(enabled);
}
///
Map<String, dynamic> getDynamicReselectionStatus() {
return {
'enabled': homeController.isCountryReselectionEnabled.value,
'currentCountry': homeController.currentSelectedCountry.value,
'latencyThreshold': homeController.countryReselectionLatencyThreshold,
};
}
}

View File

@ -42,19 +42,22 @@ class HINodeListView extends GetView<HINodeListController> {
/// ms
/// 0
int getFastestNodeDelay(
HINodeListController controller, List<KROutboundItem> outboundList) {
HINodeListController controller,
List<KROutboundItem> outboundList,
) {
if (outboundList.isEmpty) return 0;
int fastestDelay = _getDisplayDelay(controller, outboundList.first);
// 65535
final validNodes = outboundList.where((node) {
final delay = node.urlTestDelay.value;
return delay > 0 && delay < 65535;
}).toList();
for (final item in outboundList.skip(1)) {
final delay = _getDisplayDelay(controller, item);
if (delay < fastestDelay) {
fastestDelay = delay;
}
}
if (validNodes.isEmpty) return 0;
return fastestDelay;
//
validNodes.sort((a, b) => a.urlTestDelay.value.compareTo(b.urlTestDelay.value));
return validNodes.first.urlTestDelay.value;
}
///
@ -109,11 +112,19 @@ class HINodeListView extends GetView<HINodeListController> {
_buildEmptyListPlaceholder(context, AppTranslations.kr_home.noRegions)
else ...[
InkWell(
onTap: () {
controller.homeController.kr_selectNode('auto');
// 🔧 async
onTap: () async {
try {
final success =
await controller.homeController.kr_performNodeSwitch('auto');
if (success) {
controller.homeController.kr_currentListStatus.value =
KRHomeViewsListStatus.kr_none;
controller.homeController.kr_coutryText.value = 'auto';
}
} catch (e) {
KRLogUtil.kr_e('Auto选项切换异常: $e',
tag: 'NodeListView');
}
},
child: Container(
decoration: BoxDecoration(
@ -189,14 +200,7 @@ class HINodeListView extends GetView<HINodeListController> {
return InkWell(
onTap: () {
//
final item = findFastestNode(country.outboundList);
KRLogUtil.kr_i(item.tag);
print('item${item.toString()}');
controller.homeController.kr_selectNode(item.tag);
controller.homeController.kr_currentListStatus.value =
KRHomeViewsListStatus.kr_none;
controller.homeController.kr_coutryText.value = country.country;
controller.homeController.onCountrySelected(country.country);
},
child: _kr_buildCountryListItem(context, country: country),
);

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
@ -115,7 +116,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
//
final RxString currentSelectedCountry = ''.obs;
final RxBool isCountryReselectionEnabled = true.obs;
final int countryReselectionLatencyThreshold = 3000; //
//
@ -601,8 +601,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
// UI
update();
//
_checkCountryReselection(value);
} catch (e) {
KRLogUtil.kr_e('处理活动组时发生错误: $e', tag: 'HomeController');
// 🔧 0
@ -632,34 +630,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
kr_updateMarkers(updateTags);
}
});
//
/**
ever(KRLanguageUtils.kr_language, (_) {
KRLogUtil.kr_i('🌐 语言变化,更新连接文本', tag: 'HomeController');
kr_connectText.value = "";
switch (KRSingBoxImp.instance.kr_status.value) {
case SingboxStopped():
kr_connectText.value = AppTranslations.kr_home.disconnected;
kr_currentIp.value = "--";
kr_currentProtocol.value = "--";
break;
case SingboxStarting():
kr_connectText.value = AppTranslations.kr_home.connecting;
break;
case SingboxStarted():
kr_connectText.value = AppTranslations.kr_home.connected;
break;
case SingboxStopping():
kr_connectText.value = AppTranslations.kr_home.disconnecting;
break;
}
// UI
update();
});
***/
}
/// 🔧 : hiddify-app toggleConnection
@ -946,79 +916,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
return node.urlTestDelay.value < 65535 && node.urlTestDelay.value > 0;
}
///
void _checkCountryReselection(List<dynamic> activeGroups) {
//
if (!isCountryReselectionEnabled.value) {
return;
}
//
if (!kr_isConnected.value) {
return;
}
//
if (currentSelectedCountry.isEmpty) {
return;
}
try {
//
final currentNodeInfo = kr_getRealConnectedNodeInfo();
final currentDelay = currentNodeInfo['delay'] as int;
//
if (currentDelay > 0 && currentDelay < countryReselectionLatencyThreshold) {
//
return;
}
//
if (currentDelay >= countryReselectionLatencyThreshold || currentDelay <= 0) {
KRLogUtil.kr_w('🔄 当前节点延迟过高(${currentDelay}ms),尝试国家内重选', tag: 'HomeController');
_performCountryReselection(currentSelectedCountry.value);
}
} catch (e) {
KRLogUtil.kr_e('检查国家内重选时出错: $e', tag: 'HomeController');
}
}
///
void _performCountryReselection(String country) {
try {
//
final countryNodes = kr_subscribeService.allList
.where((node) => node.country == country && node.tag != 'auto')
.toList();
if (countryNodes.isEmpty) {
KRLogUtil.kr_w('⚠️ 国家 $country 内没有可用节点', tag: 'HomeController');
return;
}
//
String? bestNode;
int minDelay = 65535;
for (var node in countryNodes) {
final delay = node.urlTestDelay.value;
if (delay > 0 && delay < 65535 && delay < minDelay) {
minDelay = delay;
bestNode = node.tag;
}
}
if (bestNode != null && bestNode != kr_cutSeletedTag.value) {
KRLogUtil.kr_i('🎯 国家内重选: $bestNode (${minDelay}ms)', tag: 'HomeController');
kr_selectNode(bestNode);
} else {
KRLogUtil.kr_w('⚠️ 国家 $country 内没有更好的节点可选', tag: 'HomeController');
}
} catch (e) {
KRLogUtil.kr_e('执行国家内重选时出错: $e', tag: 'HomeController');
}
}
/// hi_node_list_controller调用
void setCurrentSelectedCountry(String country) {
@ -1026,66 +923,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
KRLogUtil.kr_i('🌍 设置当前选择国家: $country', tag: 'HomeController');
}
/// /
void setCountryReselectionEnabled(bool enabled) {
isCountryReselectionEnabled.value = enabled;
KRLogUtil.kr_i('🔄 国家内重选功能已${enabled ? "启用" : "禁用"}', tag: 'HomeController');
}
///
Map<String, dynamic> getCurrentCountryNodeStats() {
if (currentSelectedCountry.isEmpty) {
return {'error': '没有选择国家'};
}
final countryNodes = kr_subscribeService.allList
.where((node) => node.country == currentSelectedCountry.value && node.tag != 'auto')
.toList();
if (countryNodes.isEmpty) {
return {'error': '国家内没有节点'};
}
List<int> validDelays = [];
int totalNodes = countryNodes.length;
int validNodes = 0;
int minDelay = 65535;
int maxDelay = 0;
String? fastestNode;
String? slowestNode;
for (var node in countryNodes) {
final delay = node.urlTestDelay.value;
if (delay > 0 && delay < 65535) {
validDelays.add(delay);
validNodes++;
if (delay < minDelay) {
minDelay = delay;
fastestNode = node.tag;
}
if (delay > maxDelay) {
maxDelay = delay;
slowestNode = node.tag;
}
}
}
double avgDelay = validDelays.isEmpty ? 0 : validDelays.reduce((a, b) => a + b) / validDelays.length;
return {
'country': currentSelectedCountry.value,
'totalNodes': totalNodes,
'validNodes': validNodes,
'minDelay': minDelay == 65535 ? 0 : minDelay,
'maxDelay': maxDelay,
'avgDelay': avgDelay.round(),
'fastestNode': fastestNode,
'slowestNode': slowestNode,
'validDelays': validDelays,
};
}
///
void _kr_updateAutoLatency(dynamic element) {
@ -1138,8 +975,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
if (!kr_isConnected.value) {
KRLogUtil.kr_i('📴 VPN未连接只更新UI变量: $tag', tag: 'HomeController');
kr_cutSeletedTag.value = tag;
kr_updateConnectionInfo();
kr_moveToSelectedNode();
// kr_moveToSelectedNode();
// 🔧 便VPN启动时应用
KRLogUtil.kr_i('💾 保存节点选择以便稍后应用: $tag', tag: 'HomeController');
@ -1215,6 +1051,11 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
}
}
Future<void> onCountrySelected(String countryTag) async {
await KRSingBoxImp.instance.kr_selectCountry(countryTag);
KRCommonUtil.kr_showToast('已切换到 $countryTag 节点组');
}
/// 🔧 kr_selectNode
/// kr_performNodeSwitch
///
@ -1309,7 +1150,9 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
}
// auto urltest
print('当前活动组----${KRSingBoxImp.instance.kr_activeGroups.length}');
for (var group in KRSingBoxImp.instance.kr_activeGroups) {
print('当前活动组----$group}');
if (group.type == ProxyType.urltest) {
final selectedNode = group.selected;
final node = kr_subscribeService.keyList[selectedNode];
@ -1320,6 +1163,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
};
}
}
print('hhhhhh${kr_subscribeService.keyList}', );
// urltest
return {
@ -1344,7 +1188,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
///
String kr_getRealConnectedNodeCountry() {
final info = kr_getRealConnectedNodeInfo();
final delay = info['delay'];
final delay = kr_currentNodeLatency.value;
final country = kr_getCountryFullName(info['country']);
if (delay == -2) {
return '--';
@ -1722,45 +1566,75 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
}
}
///
Future<void> _kr_testSingleNode(dynamic item) async {
final stopwatch = Stopwatch()..start();
try {
//
String address = item.serverAddr;
int port = 443; //
// serverAddr
if (item.serverAddr.contains(':')) {
final parts = item.serverAddr.split(':');
address = parts[0];
port = int.tryParse(parts[1]) ?? 443;
} else if (item.config['server_port'] != null) {
port = item.config['server_port'];
}
// TCP
final socket = await Socket.connect(
address,
port,
timeout: const Duration(seconds: 8),
);
final delay = stopwatch.elapsedMilliseconds;
socket.destroy();
// 5
if (delay > 5000) {
item.urlTestDelay.value = 65535;
} else {
item.urlTestDelay.value = delay;
}
print('✅ 节点 ${item.tag} 测试完成,延迟: ${item.urlTestDelay.value}ms');
} catch (e) {
//
item.urlTestDelay.value = 65535;
print('⚠️ 节点 ${item.tag} 测试失败: $e');
}
}
/// 使
Future<void> _kr_testLatencyWithoutVpn() async {
kr_isLatency.value = true;
try {
KRLogUtil.kr_i(
'🔌 开始未连接状态延迟测试(界面显示随机延迟)', tag: 'HomeController');
KRLogUtil.kr_i(
'📊 当前连接状态: ${kr_isConnected.value}', tag: 'HomeController');
KRLogUtil.kr_i('🎲 界面将显示30ms-100ms的随机延迟不影响其他逻辑',
tag: 'HomeController');
// auto节点
try {
KRLogUtil.kr_i('🔌 开始未连接状态延迟测试(真实测速)', tag: 'HomeController');
KRLogUtil.kr_i('📊 当前连接状态: ${kr_isConnected.value}', tag: 'HomeController');
// auto
final testableNodes = kr_subscribeService.allList
.where((item) => item.tag != 'auto')
.toList();
KRLogUtil.kr_i(
'📋 找到 ${testableNodes.length} 个可测试节点', tag: 'HomeController');
KRLogUtil.kr_i('📋 找到 ${testableNodes.length} 个可测试节点', tag: 'HomeController');
if (testableNodes.isEmpty) {
KRLogUtil.kr_w('⚠️ 没有可测试的节点', tag: 'HomeController');
return;
}
// urlTestDelay
KRLogUtil.kr_i(
'✅ 延迟显示将由界面层处理,不影响节点选择逻辑', tag: 'NodeTest');
//
for (var node in testableNodes) {
await _kr_testSingleNode(node); //
KRLogUtil.kr_i('节点 ${node.tag} 延迟: ${node.urlTestDelay.value}ms', tag: 'HomeController');
}
//
final successCount = testableNodes
.where((item) => item.urlTestDelay.value < 65535)
.length;
final failCount = testableNodes.length - successCount;
KRLogUtil.kr_i('✅ 本机网络延迟测试完成', tag: 'HomeController');
KRLogUtil.kr_i('📊 测试结果: 成功 $successCount 个,失败 $failCount',
tag: 'HomeController');
//
// 便
final sortedNodes = testableNodes
.where((item) => item.urlTestDelay.value < 65535)
.toList()
@ -1770,11 +1644,11 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
KRLogUtil.kr_i('🏆 延迟最低的前3个节点:', tag: 'HomeController');
for (int i = 0; i < 3 && i < sortedNodes.length; i++) {
final node = sortedNodes[i];
KRLogUtil.kr_i(
' ${i + 1}. ${node.tag}: ${node.urlTestDelay.value}ms',
tag: 'HomeController');
KRLogUtil.kr_i(' ${i + 1}. ${node.tag}: ${node.urlTestDelay.value}ms', tag: 'HomeController');
}
}
KRLogUtil.kr_i('✅ 本机网络延迟测试完成', tag: 'HomeController');
} catch (e) {
KRLogUtil.kr_e('❌ 本机网络延迟测试过程出错: $e', tag: 'HomeController');
} finally {
@ -1782,7 +1656,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
}
}
///
void kr_startConnectionTimer() {
kr_stopConnectionTimer();
@ -1989,14 +1862,4 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
return false;
}
}
///
void checkCountryReselection(List<dynamic> activeGroups) {
_checkCountryReselection(activeGroups);
}
///
void performCountryReselection(String country) {
_performCountryReselection(country);
}
}

View File

@ -609,6 +609,8 @@ class KRSingBoxImp {
///
void _kr_subscribeToGroups() {
KRLogUtil.kr_i('🛰 启动分组监听 watchActiveGroups / watchGroups', tag: 'SingBox');
//
for (var sub in _kr_subscriptions) {
if (sub.hashCode.toString().contains('Groups')) {
@ -1423,6 +1425,87 @@ class KRSingBoxImp {
}
}
/// 🌍
/// 'auto' kr_selectOutbound('auto')
///
Future<void> kr_selectCountry(String country) async {
KRLogUtil.kr_i('🌎 [v2.1] 开始选择国家: $country', tag: 'SingBox');
if (country == 'auto') {
KRLogUtil.kr_i('🌀 国家为 auto执行自动节点选择逻辑', tag: 'SingBox');
await kr_selectOutbound('auto');
return;
}
if (kr_activeGroups.isEmpty) {
KRLogUtil.kr_w('⚠️ kr_activeGroups 当前为空,无法进行国家选择', tag: 'SingBox');
return;
}
/*final selectGroup = kr_activeGroups.firstWhere(
(group) => group.tag == 'select',
orElse: () => throw Exception('未找到 "select" 组'),
);
final countryNodes = selectGroup.items.where((item) {
final nodeCountry = item.options?['country'] ?? '';
return nodeCountry.toString().toLowerCase() == country.toLowerCase();
}).toList();
if (countryNodes.isEmpty) {
KRLogUtil.kr_w('⚠️ 未找到该国家的节点: $country', tag: 'SingBox');
for (var item in selectGroup.items) {
KRLogUtil.kr_d(
' 可用节点: ${item.tag} (${item.options?['country'] ?? '未知'})',
tag: 'SingBox',
);
}
return;
}
KRLogUtil.kr_i('🇨🇭 找到 ${countryNodes.length} 个属于 "$country" 的节点', tag: 'SingBox');
// 9999
countryNodes.sort((a, b) =>
(a.urlTestDelay ?? 9999).compareTo(b.urlTestDelay ?? 9999));
final fastestNode = countryNodes.first;
final selectedTag = fastestNode.tag;
final delay = fastestNode.urlTestDelay ?? -1;
KRLogUtil.kr_i('✅ 选中该国家最快节点: $selectedTag (${delay}ms)', tag: 'SingBox');
try {
await KRSecureStorage().kr_saveData(key: 'selected_country', value: country);
await KRSecureStorage().kr_saveData(key: _keySelectedNode, value: selectedTag);
} catch (e) {
KRLogUtil.kr_w('⚠️ 保存国家/节点信息失败: $e', tag: 'SingBox');
}
try {
await _kr_ensureCommandClientInitialized();
await _kr_selectOutboundWithRetry('select', selectedTag,
maxAttempts: 3, initialDelay: 100);
KRLogUtil.kr_i('✅ 国家内节点切换成功: $selectedTag', tag: 'SingBox');
} catch (e) {
KRLogUtil.kr_e('❌ 国家内节点选择失败: $e', tag: 'SingBox');
rethrow;
}
_nodeSelectionTimer?.cancel();
KRLogUtil.kr_i('🔁 启动定时器防止节点被 auto 覆盖', tag: 'SingBox');
_nodeSelectionTimer =
Timer.periodic(const Duration(seconds: 20), (timer) async {
try {
await kr_singBox.selectOutbound('select', selectedTag).run();
KRLogUtil.kr_d('🔁 定时确认国家内节点: $selectedTag', tag: 'SingBox');
} catch (e) {
KRLogUtil.kr_w('🔁 定时确认失败: $e', tag: 'SingBox');
}
});*/
}
///
Directory get directory =>