mirror of
https://ghfast.top/https://github.com/StarCitizenToolBox/app.git
synced 2025-06-30 04:31:40 +08:00
将诊断功能从首页拆分至独立页面
This commit is contained in:
172
lib/ui/home/game_doctor/game_doctor_ui.dart
Normal file
172
lib/ui/home/game_doctor/game_doctor_ui.dart
Normal file
@ -0,0 +1,172 @@
|
||||
import 'package:flutter_tilt/flutter_tilt.dart';
|
||||
import 'package:starcitizen_doctor/base/ui.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import 'game_doctor_ui_model.dart';
|
||||
|
||||
class GameDoctorUI extends BaseUI<GameDoctorUIModel> {
|
||||
@override
|
||||
Widget? buildBody(BuildContext context, GameDoctorUIModel model) {
|
||||
return makeDefaultPage(context, model,
|
||||
content: Stack(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
if (model.isChecking)
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 12),
|
||||
Text(model.lastScreenInfo)
|
||||
],
|
||||
),
|
||||
))
|
||||
else if (model.checkResult == null ||
|
||||
model.checkResult!.isEmpty) ...[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
const Text("扫描完毕,没有找到问题!", maxLines: 1),
|
||||
const SizedBox(height: 64),
|
||||
makeRescueBanner(context),
|
||||
],
|
||||
),
|
||||
))
|
||||
] else ...[
|
||||
...makeResult(context, model),
|
||||
const SizedBox(height: 64),
|
||||
makeRescueBanner(context),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (model.isFixing)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withAlpha(150),
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 12),
|
||||
Text(model.isFixingString.isNotEmpty
|
||||
? model.isFixingString
|
||||
: "正在处理..."),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
List<Widget> makeResult(BuildContext context, GameDoctorUIModel model) {
|
||||
return [
|
||||
const SizedBox(height: 24),
|
||||
const Text(
|
||||
"检测结果",
|
||||
style: TextStyle(fontSize: 24),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(model.lastScreenInfo, maxLines: 1),
|
||||
const SizedBox(height: 24),
|
||||
ListView.builder(
|
||||
itemCount: model.checkResult!.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = model.checkResult![index];
|
||||
return makeResultItem(item, model);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
"注意:本工具检测结果仅供参考,若您不理解以上操作,请提供截图给有经验的玩家!",
|
||||
style: TextStyle(color: Colors.red, fontSize: 16),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Widget makeRescueBanner(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
await showToast(context,
|
||||
"您即将前往由 深空治疗中心(QQ群号:536454632 ) 提供的游戏异常救援服务,主要解决游戏安装失败与频繁闪退,如游戏玩法问题,请勿加群。");
|
||||
launchUrlString(
|
||||
"https://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=-M4wEme_bCXbUGT4LFKLH0bAYTFt70Ad&authKey=vHVr0TNgRmKu%2BHwywoJV6EiLa7La2VX74Vkyixr05KA0H9TqB6qWlCdY%2B9jLQ4Ha&noverify=0&group_code=536454632");
|
||||
},
|
||||
child: Tilt(
|
||||
shadowConfig: const ShadowConfig(maxIntensity: .2),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context).cardColor,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Image.asset("assets/rescue.png", width: 24, height: 24),
|
||||
const SizedBox(width: 12),
|
||||
const Text("需要帮助? 点击加群获得免费人工支援!"),
|
||||
],
|
||||
),
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeResultItem(
|
||||
MapEntry<String, String> item, GameDoctorUIModel model) {
|
||||
final errorNames = {
|
||||
"unSupport_system":
|
||||
MapEntry("不支持的操作系统,游戏可能无法运行", "请升级您的系统 (${item.value})"),
|
||||
"no_live_path": MapEntry("安装目录缺少LIVE文件夹,可能导致安装失败",
|
||||
"点击修复为您创建 LIVE 文件夹,完成后重试安装。(${item.value})"),
|
||||
"nvme_PhysicalBytes": MapEntry("新型 NVME 设备,与 RSI 启动器暂不兼容,可能导致安装失败",
|
||||
"为注册表项添加 ForcedPhysicalSectorSizeInBytes 值 模拟旧设备。硬盘分区(${item.value})"),
|
||||
"eac_file_miss": const MapEntry("EasyAntiCheat 文件丢失",
|
||||
"未在 LIVE 文件夹找到 EasyAntiCheat 文件 或 文件不完整,请使用 RSI 启动器校验文件"),
|
||||
"eac_not_install": const MapEntry("EasyAntiCheat 未安装 或 未正常退出",
|
||||
"EasyAntiCheat 未安装,请点击修复为您一键安装。(在游戏正常启动并结束前,该问题会一直出现,若您因为其他原因游戏闪退,可忽略此条目)"),
|
||||
"cn_user_name":
|
||||
const MapEntry("中文用户名!", "中文用户名可能会导致游戏启动/安装错误! 点击修复按钮查看修改教程!"),
|
||||
"cn_install_path": MapEntry("中文安装路径!",
|
||||
"中文安装路径!这可能会导致游戏 启动/安装 错误!(${item.value}),请在RSI启动器更换安装路径。"),
|
||||
"low_ram": MapEntry(
|
||||
"物理内存过低", "您至少需要 16GB 的物理内存(Memory)才可运行此游戏。(当前大小:${item.value})"),
|
||||
};
|
||||
return ListTile(
|
||||
title: Text(errorNames[item.key]?.key ?? item.key),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.only(top: 4, bottom: 4),
|
||||
child: Text("修复建议: ${errorNames[item.key]?.value ?? "暂无解决方法,请截图反馈"}"),
|
||||
),
|
||||
trailing: Button(
|
||||
onPressed: (errorNames[item.key]?.value == null || model.isFixing)
|
||||
? null
|
||||
: () async {
|
||||
await model.doFix(item);
|
||||
model.isFixing = false;
|
||||
model.notifyListeners();
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
|
||||
child: Text("修复"),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String getUITitle(BuildContext context, GameDoctorUIModel model) =>
|
||||
"一键诊断 -> ${model.scInstalledPath}";
|
||||
}
|
224
lib/ui/home/game_doctor/game_doctor_ui_model.dart
Normal file
224
lib/ui/home/game_doctor/game_doctor_ui_model.dart
Normal file
@ -0,0 +1,224 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:starcitizen_doctor/base/ui_model.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class GameDoctorUIModel extends BaseUIModel {
|
||||
String scInstalledPath = "";
|
||||
|
||||
GameDoctorUIModel(this.scInstalledPath);
|
||||
|
||||
String _lastScreenInfo = "";
|
||||
|
||||
String get lastScreenInfo => _lastScreenInfo;
|
||||
|
||||
List<MapEntry<String, String>>? checkResult;
|
||||
|
||||
set lastScreenInfo(String info) {
|
||||
_lastScreenInfo = info;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool isChecking = false;
|
||||
|
||||
bool isFixing = false;
|
||||
String isFixingString = "";
|
||||
|
||||
final cnExp = RegExp(r"[^\x00-\xff]");
|
||||
|
||||
@override
|
||||
void initModel() {
|
||||
doCheck()?.call();
|
||||
super.initModel();
|
||||
}
|
||||
|
||||
VoidCallback? doCheck() {
|
||||
if (isChecking) return null;
|
||||
return () async {
|
||||
isChecking = true;
|
||||
lastScreenInfo = "正在分析...";
|
||||
await _statCheck();
|
||||
isChecking = false;
|
||||
notifyListeners();
|
||||
};
|
||||
}
|
||||
|
||||
Future _statCheck() async {
|
||||
checkResult = [];
|
||||
await _checkPreInstall();
|
||||
await _checkEAC();
|
||||
// TODO for debug
|
||||
|
||||
// checkResult?.add(const MapEntry("unSupport_system", "android"));
|
||||
// checkResult?.add(const MapEntry("nvme_PhysicalBytes", "C"));
|
||||
// checkResult?.add(const MapEntry("no_live_path", ""));
|
||||
|
||||
if (checkResult!.isEmpty) {
|
||||
checkResult = null;
|
||||
lastScreenInfo = "分析完毕,没有发现问题";
|
||||
} else {
|
||||
lastScreenInfo = "分析完毕,发现 ${checkResult!.length} 个问题";
|
||||
}
|
||||
|
||||
if (scInstalledPath == "not_install" && (checkResult?.isEmpty ?? true)) {
|
||||
showToast(context!, "扫描完毕,没有发现问题,若仍然安装失败,请尝试使用工具箱中的 RSI启动器管理员模式。");
|
||||
}
|
||||
}
|
||||
|
||||
Future _checkEAC() async {
|
||||
if (scInstalledPath == "not_install") return;
|
||||
lastScreenInfo = "正在检查:EAC";
|
||||
final eacPath = "$scInstalledPath\\EasyAntiCheat";
|
||||
final eacJsonPath = "$eacPath\\Settings.json";
|
||||
if (!await Directory(eacPath).exists() ||
|
||||
!await File(eacJsonPath).exists()) {
|
||||
checkResult?.add(const MapEntry("eac_file_miss", ""));
|
||||
return;
|
||||
}
|
||||
final eacJsonData = await File(eacJsonPath).readAsBytes();
|
||||
final Map eacJson = json.decode(utf8.decode(eacJsonData));
|
||||
final eacID = eacJson["productid"];
|
||||
final eacDeploymentId = eacJson["deploymentid"];
|
||||
if (eacID == null || eacDeploymentId == null) {
|
||||
checkResult?.add(const MapEntry("eac_file_miss", ""));
|
||||
return;
|
||||
}
|
||||
final eacFilePath =
|
||||
"${Platform.environment["appdata"]}\\EasyAntiCheat\\$eacID\\$eacDeploymentId\\anticheatlauncher.log";
|
||||
if (!await File(eacFilePath).exists()) {
|
||||
checkResult?.add(MapEntry("eac_not_install", eacPath));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future _checkPreInstall() async {
|
||||
lastScreenInfo = "正在检查:运行环境";
|
||||
if (!(Platform.operatingSystemVersion.contains("Windows 10") ||
|
||||
Platform.operatingSystemVersion.contains("Windows 11"))) {
|
||||
checkResult
|
||||
?.add(MapEntry("unSupport_system", Platform.operatingSystemVersion));
|
||||
lastScreenInfo = "不支持的操作系统:${Platform.operatingSystemVersion}";
|
||||
await showToast(context!, lastScreenInfo);
|
||||
}
|
||||
|
||||
if (cnExp.hasMatch(await SCLoggerHelper.getLogFilePath() ?? "")) {
|
||||
checkResult?.add(const MapEntry("cn_user_name", ""));
|
||||
}
|
||||
|
||||
// 检查 RAM
|
||||
final ramSize = await SystemHelper.getSystemMemorySizeGB();
|
||||
if (ramSize < 16) {
|
||||
checkResult?.add(MapEntry("low_ram", "$ramSize"));
|
||||
}
|
||||
|
||||
lastScreenInfo = "正在检查:安装信息";
|
||||
// 检查安装分区
|
||||
try {
|
||||
final listData = await SCLoggerHelper.getGameInstallPath(
|
||||
await SCLoggerHelper.getLauncherLogList() ?? []);
|
||||
final p = [];
|
||||
final checkedPath = [];
|
||||
for (var installPath in listData) {
|
||||
if (!checkedPath.contains(installPath)) {
|
||||
if (cnExp.hasMatch(installPath)) {
|
||||
checkResult?.add(MapEntry("cn_install_path", installPath));
|
||||
}
|
||||
if (scInstalledPath == "not_install") {
|
||||
checkedPath.add(installPath);
|
||||
if (!await Directory(installPath).exists()) {
|
||||
checkResult?.add(MapEntry("no_live_path", installPath));
|
||||
}
|
||||
}
|
||||
final tp = installPath.split(":")[0];
|
||||
if (!p.contains(tp)) {
|
||||
p.add(tp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call check
|
||||
for (var element in p) {
|
||||
var result = await Process.run('powershell', [
|
||||
"(fsutil fsinfo sectorinfo $element: | Select-String 'PhysicalBytesPerSectorForPerformance').ToString().Split(':')[1].Trim()"
|
||||
]);
|
||||
dPrint(result.stdout);
|
||||
if (result.stderr == "") {
|
||||
final rs = result.stdout.toString();
|
||||
final physicalBytesPerSectorForPerformance = (int.tryParse(rs) ?? 0);
|
||||
if (physicalBytesPerSectorForPerformance > 4096) {
|
||||
checkResult?.add(MapEntry("nvme_PhysicalBytes", element));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint(e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> doFix(MapEntry<String, String> item) async {
|
||||
isFixing = true;
|
||||
notifyListeners();
|
||||
switch (item.key) {
|
||||
case "unSupport_system":
|
||||
showToast(context!, "若您的硬件达标,请尝试安装最新的 Windows 系统。");
|
||||
return;
|
||||
case "no_live_path":
|
||||
try {
|
||||
await Directory(item.value).create(recursive: true);
|
||||
showToast(context!, "创建文件夹成功,请尝试继续下载游戏!");
|
||||
checkResult?.remove(item);
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
showToast(context!, "创建文件夹失败,请尝试手动创建。\n目录:${item.value} \n错误:$e");
|
||||
}
|
||||
return;
|
||||
case "nvme_PhysicalBytes":
|
||||
final r = await SystemHelper.addNvmePatch();
|
||||
if (r == "") {
|
||||
showToast(context!,
|
||||
"修复成功,请尝试重启后继续安装游戏! 若注册表修改操作导致其他软件出现兼容问题,请使用 工具 中的 NVME 注册表清理。");
|
||||
checkResult?.remove(item);
|
||||
notifyListeners();
|
||||
} else {
|
||||
showToast(context!, "修复失败,$r");
|
||||
}
|
||||
return;
|
||||
case "eac_file_miss":
|
||||
showToast(
|
||||
context!, "未在 LIVE 文件夹找到 EasyAntiCheat 文件 或 文件不完整,请使用 RSI 启动器校验文件");
|
||||
return;
|
||||
case "eac_not_install":
|
||||
final eacJsonPath = "${item.value}\\Settings.json";
|
||||
final eacJsonData = await File(eacJsonPath).readAsBytes();
|
||||
final Map eacJson = json.decode(utf8.decode(eacJsonData));
|
||||
final eacID = eacJson["productid"];
|
||||
try {
|
||||
var result = await Process.run(
|
||||
"${item.value}\\EasyAntiCheat_EOS_Setup.exe", ["install", eacID]);
|
||||
dPrint("${item.value}\\EasyAntiCheat_EOS_Setup.exe install $eacID");
|
||||
if (result.stderr == "") {
|
||||
showToast(context!, "修复成功,请尝试启动游戏。(若问题无法解决,请使用工具箱的 《重装 EAC》)");
|
||||
checkResult?.remove(item);
|
||||
notifyListeners();
|
||||
} else {
|
||||
showToast(context!, "修复失败,${result.stderr}");
|
||||
}
|
||||
} catch (e) {
|
||||
showToast(context!, "修复失败,$e");
|
||||
}
|
||||
return;
|
||||
case "cn_user_name":
|
||||
showToast(context!, "即将跳转,教程来自互联网,请谨慎操作...");
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
launchUrlString(
|
||||
"https://btfy.eu.org/?q=5L+u5pS5d2luZG93c+eUqOaIt+WQjeS7juS4reaWh+WIsOiLseaWhw==");
|
||||
return;
|
||||
default:
|
||||
showToast(context!, "该问题暂不支持自动处理,请提供截图寻求帮助");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@ -39,12 +39,7 @@ class HomeUI extends BaseUI<HomeUIModel> {
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
],
|
||||
if (!model.isChecking &&
|
||||
model.checkResult != null &&
|
||||
model.checkResult!.isNotEmpty)
|
||||
...makeResult(context, model)
|
||||
else
|
||||
...makeIndex(context, model)
|
||||
...makeIndex(context, model)
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -167,7 +162,7 @@ class HomeUI extends BaseUI<HomeUIModel> {
|
||||
onPressed: () => model.openDir(model.scInstalledPath)),
|
||||
const SizedBox(width: 12),
|
||||
Button(
|
||||
onPressed: model.isChecking ? null : model.reScanPath,
|
||||
onPressed: model.reScanPath,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(6),
|
||||
child: Icon(FluentIcons.refresh),
|
||||
@ -423,9 +418,7 @@ class HomeUI extends BaseUI<HomeUIModel> {
|
||||
itemBuilder: (context, index) {
|
||||
final item = items.elementAt(index);
|
||||
return HoverButton(
|
||||
onPressed: item.key == "auto_check" && model.isChecking
|
||||
? null
|
||||
: () => model.onMenuTap(item.key),
|
||||
onPressed: () => model.onMenuTap(item.key),
|
||||
builder: (BuildContext context, Set<ButtonStates> states) {
|
||||
return Container(
|
||||
width: 300,
|
||||
@ -478,13 +471,10 @@ class HomeUI extends BaseUI<HomeUIModel> {
|
||||
Text(model.localizationUpdateInfo?.key ?? " "),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
if (item.key == "auto_check" && model.isChecking)
|
||||
const ProgressRing()
|
||||
else
|
||||
const Icon(
|
||||
FluentIcons.chevron_right,
|
||||
size: 16,
|
||||
),
|
||||
const Icon(
|
||||
FluentIcons.chevron_right,
|
||||
size: 16,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -495,84 +485,9 @@ class HomeUI extends BaseUI<HomeUIModel> {
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> makeResult(BuildContext context, HomeUIModel model) {
|
||||
return [
|
||||
const SizedBox(height: 24),
|
||||
const Text(
|
||||
"检测结果",
|
||||
style: TextStyle(fontSize: 20),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(model.lastScreenInfo, maxLines: 1),
|
||||
const SizedBox(height: 24),
|
||||
ListView.builder(
|
||||
itemCount: model.checkResult!.length,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = model.checkResult![index];
|
||||
return makeResultItem(item, model);
|
||||
},
|
||||
),
|
||||
Text(
|
||||
"注意:本工具检测结果仅供参考,若您不理解以上操作,请提供截图给有经验的玩家!",
|
||||
style: TextStyle(color: Colors.red, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 64),
|
||||
FilledButton(
|
||||
onPressed: model.resetCheck,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
|
||||
child: Text('返回'),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 38),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
String getUITitle(BuildContext context, HomeUIModel model) => "HOME";
|
||||
|
||||
Widget makeResultItem(MapEntry<String, String> item, HomeUIModel model) {
|
||||
final errorNames = {
|
||||
"unSupport_system":
|
||||
MapEntry("不支持的操作系统,游戏可能无法运行", "请升级您的系统 (${item.value})"),
|
||||
"no_live_path": MapEntry("安装目录缺少LIVE文件夹,可能导致安装失败",
|
||||
"点击修复为您创建 LIVE 文件夹,完成后重试安装。(${item.value})"),
|
||||
"nvme_PhysicalBytes": MapEntry("新型 NVME 设备,与 RSI 启动器暂不兼容,可能导致安装失败",
|
||||
"为注册表项添加 ForcedPhysicalSectorSizeInBytes 值 模拟旧设备。硬盘分区(${item.value})"),
|
||||
"eac_file_miss": const MapEntry("EasyAntiCheat 文件丢失",
|
||||
"未在 LIVE 文件夹找到 EasyAntiCheat 文件 或 文件不完整,请使用 RSI 启动器校验文件"),
|
||||
"eac_not_install": const MapEntry("EasyAntiCheat 未安装 或 未正常退出",
|
||||
"EasyAntiCheat 未安装,请点击修复为您一键安装。(在游戏正常启动并结束前,该问题会一直出现,若您因为其他原因游戏闪退,可忽略此条目)"),
|
||||
"cn_user_name":
|
||||
const MapEntry("中文用户名!", "中文用户名可能会导致游戏启动/安装错误! 点击修复按钮查看修改教程!"),
|
||||
"cn_install_path": MapEntry("中文安装路径!",
|
||||
"中文安装路径!这可能会导致游戏 启动/安装 错误!(${item.value}),请在RSI启动器更换安装路径。"),
|
||||
"low_ram": MapEntry(
|
||||
"物理内存过低", "您至少需要 16GB 的物理内存(Memory)才可运行此游戏。(当前大小:${item.value})"),
|
||||
};
|
||||
return ListTile(
|
||||
title: Text(errorNames[item.key]?.key ?? item.key),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.only(top: 4, bottom: 4),
|
||||
child: Text("修复建议: ${errorNames[item.key]?.value ?? "暂无解决方法,请截图反馈"}"),
|
||||
),
|
||||
trailing: Button(
|
||||
onPressed: (errorNames[item.key]?.value == null || model.isFixing)
|
||||
? null
|
||||
: () async {
|
||||
await model.doFix(item);
|
||||
model.isFixing = false;
|
||||
model.notifyListeners();
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(left: 8, right: 8),
|
||||
child: Text("修复"),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeWebViewButton(HomeUIModel model,
|
||||
{required Widget icon,
|
||||
required String name,
|
||||
|
@ -33,6 +33,8 @@ import 'package:html/dom.dart' as html_dom;
|
||||
import 'package:windows_ui/windows_ui.dart';
|
||||
|
||||
import 'countdown/countdown_dialog_ui.dart';
|
||||
import 'game_doctor/game_doctor_ui.dart';
|
||||
import 'game_doctor/game_doctor_ui_model.dart';
|
||||
import 'localization/localization_ui.dart';
|
||||
import 'performance/performance_ui.dart';
|
||||
import 'webview/webview_localization_capture_ui.dart';
|
||||
@ -42,11 +44,7 @@ class HomeUIModel extends BaseUIModel {
|
||||
|
||||
List<String> scInstallPaths = [];
|
||||
|
||||
String _lastScreenInfo = "";
|
||||
|
||||
String get lastScreenInfo => _lastScreenInfo;
|
||||
|
||||
bool isChecking = false;
|
||||
String lastScreenInfo = "";
|
||||
|
||||
bool isFixing = false;
|
||||
String isFixingString = "";
|
||||
@ -58,13 +56,6 @@ class HomeUIModel extends BaseUIModel {
|
||||
List<RssItem>? rssVideoItems;
|
||||
List<RssItem>? rssTextItems;
|
||||
|
||||
set lastScreenInfo(String info) {
|
||||
_lastScreenInfo = info;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<MapEntry<String, String>>? checkResult;
|
||||
|
||||
AppWebLocalizationVersionsData? appWebLocalizationVersionsData;
|
||||
|
||||
List<CountdownFestivalItemData>? countdownFestivalListData;
|
||||
@ -73,8 +64,6 @@ class HomeUIModel extends BaseUIModel {
|
||||
|
||||
bool _isSendLocalizationUpdateNotification = false;
|
||||
|
||||
final cnExp = RegExp(r"[^\x00-\xff]");
|
||||
|
||||
AppPlacardData? appPlacardData;
|
||||
|
||||
List? scServerStatus;
|
||||
@ -194,211 +183,24 @@ class HomeUIModel extends BaseUIModel {
|
||||
}
|
||||
}
|
||||
|
||||
VoidCallback? doCheck() {
|
||||
if (isChecking) return null;
|
||||
return () async {
|
||||
isChecking = true;
|
||||
lastScreenInfo = "正在分析...";
|
||||
await _statCheck();
|
||||
isChecking = false;
|
||||
notifyListeners();
|
||||
};
|
||||
}
|
||||
|
||||
Future _statCheck() async {
|
||||
checkResult = [];
|
||||
await _checkPreInstall();
|
||||
await _checkEAC();
|
||||
// TODO for debug
|
||||
// checkResult?.add(const MapEntry("unSupport_system", "android"));
|
||||
// checkResult?.add(const MapEntry("nvme_PhysicalBytes", "c"));
|
||||
// checkResult?.add(const MapEntry("no_live_path", ""));
|
||||
|
||||
if (checkResult!.isEmpty) {
|
||||
checkResult = null;
|
||||
lastScreenInfo = "分析完毕,没有发现问题";
|
||||
} else {
|
||||
lastScreenInfo = "分析完毕,发现 ${checkResult!.length} 个问题";
|
||||
}
|
||||
|
||||
if (scInstalledPath == "not_install" && (checkResult?.isEmpty ?? true)) {
|
||||
showToast(context!, "扫描完毕,没有发现问题,若仍然安装失败,请尝试使用工具箱中的 RSI启动器管理员模式。");
|
||||
}
|
||||
}
|
||||
|
||||
Future _checkEAC() async {
|
||||
if (scInstalledPath == "not_install") return;
|
||||
lastScreenInfo = "正在检查:EAC";
|
||||
final eacPath = "$scInstalledPath\\EasyAntiCheat";
|
||||
final eacJsonPath = "$eacPath\\Settings.json";
|
||||
if (!await Directory(eacPath).exists() ||
|
||||
!await File(eacJsonPath).exists()) {
|
||||
checkResult?.add(const MapEntry("eac_file_miss", ""));
|
||||
return;
|
||||
}
|
||||
final eacJsonData = await File(eacJsonPath).readAsBytes();
|
||||
final Map eacJson = json.decode(utf8.decode(eacJsonData));
|
||||
final eacID = eacJson["productid"];
|
||||
final eacDeploymentId = eacJson["deploymentid"];
|
||||
if (eacID == null || eacDeploymentId == null) {
|
||||
checkResult?.add(const MapEntry("eac_file_miss", ""));
|
||||
return;
|
||||
}
|
||||
final eacFilePath =
|
||||
"${Platform.environment["appdata"]}\\EasyAntiCheat\\$eacID\\$eacDeploymentId\\anticheatlauncher.log";
|
||||
if (!await File(eacFilePath).exists()) {
|
||||
checkResult?.add(MapEntry("eac_not_install", eacPath));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future _checkPreInstall() async {
|
||||
lastScreenInfo = "正在检查:运行环境";
|
||||
if (!(Platform.operatingSystemVersion.contains("Windows 10") ||
|
||||
Platform.operatingSystemVersion.contains("Windows 11"))) {
|
||||
checkResult
|
||||
?.add(MapEntry("unSupport_system", Platform.operatingSystemVersion));
|
||||
lastScreenInfo = "不支持的操作系统:${Platform.operatingSystemVersion}";
|
||||
await showToast(context!, lastScreenInfo);
|
||||
}
|
||||
|
||||
if (cnExp.hasMatch(await SCLoggerHelper.getLogFilePath() ?? "")) {
|
||||
checkResult?.add(const MapEntry("cn_user_name", ""));
|
||||
}
|
||||
|
||||
// 检查 RAM
|
||||
final ramSize = await SystemHelper.getSystemMemorySizeGB();
|
||||
if (ramSize < 16) {
|
||||
checkResult?.add(MapEntry("low_ram", "$ramSize"));
|
||||
}
|
||||
|
||||
lastScreenInfo = "正在检查:安装信息";
|
||||
// 检查安装分区
|
||||
try {
|
||||
final listData = await SCLoggerHelper.getGameInstallPath(
|
||||
await SCLoggerHelper.getLauncherLogList() ?? []);
|
||||
final p = [];
|
||||
final checkedPath = [];
|
||||
for (var installPath in listData) {
|
||||
if (!checkedPath.contains(installPath)) {
|
||||
if (cnExp.hasMatch(installPath)) {
|
||||
checkResult?.add(MapEntry("cn_install_path", installPath));
|
||||
}
|
||||
if (scInstalledPath == "not_install") {
|
||||
checkedPath.add(installPath);
|
||||
if (!await Directory(installPath).exists()) {
|
||||
checkResult?.add(MapEntry("no_live_path", installPath));
|
||||
}
|
||||
}
|
||||
final tp = installPath.split(":")[0];
|
||||
if (!p.contains(tp)) {
|
||||
p.add(tp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call check
|
||||
for (var element in p) {
|
||||
var result = await Process.run('powershell', [
|
||||
"(fsutil fsinfo sectorinfo $element: | Select-String 'PhysicalBytesPerSectorForPerformance').ToString().Split(':')[1].Trim()"
|
||||
]);
|
||||
dPrint(result.stdout);
|
||||
if (result.stderr == "") {
|
||||
final rs = result.stdout.toString();
|
||||
final physicalBytesPerSectorForPerformance = (int.tryParse(rs) ?? 0);
|
||||
if (physicalBytesPerSectorForPerformance > 4096) {
|
||||
checkResult?.add(MapEntry("nvme_PhysicalBytes", element));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint(e);
|
||||
}
|
||||
}
|
||||
|
||||
void resetCheck() {
|
||||
checkResult = null;
|
||||
reScanPath();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> doFix(MapEntry<String, String> item) async {
|
||||
isFixing = true;
|
||||
notifyListeners();
|
||||
switch (item.key) {
|
||||
case "unSupport_system":
|
||||
showToast(context!, "若您的硬件达标,请尝试安装最新的 Windows 系统。");
|
||||
return;
|
||||
case "no_live_path":
|
||||
try {
|
||||
await Directory(item.value).create(recursive: true);
|
||||
showToast(context!, "创建文件夹成功,请尝试继续下载游戏!");
|
||||
checkResult?.remove(item);
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
showToast(context!, "创建文件夹失败,请尝试手动创建。\n目录:${item.value} \n错误:$e");
|
||||
}
|
||||
return;
|
||||
case "nvme_PhysicalBytes":
|
||||
final r = await SystemHelper.addNvmePatch();
|
||||
if (r == "") {
|
||||
showToast(context!,
|
||||
"修复成功,请尝试重启后继续安装游戏! 若注册表修改操作导致其他软件出现兼容问题,请使用 工具 中的 NVME 注册表清理。");
|
||||
checkResult?.remove(item);
|
||||
notifyListeners();
|
||||
} else {
|
||||
showToast(context!, "修复失败,$r");
|
||||
}
|
||||
return;
|
||||
case "eac_file_miss":
|
||||
showToast(
|
||||
context!, "未在 LIVE 文件夹找到 EasyAntiCheat 文件 或 文件不完整,请使用 RSI 启动器校验文件");
|
||||
return;
|
||||
case "eac_not_install":
|
||||
final eacJsonPath = "${item.value}\\Settings.json";
|
||||
final eacJsonData = await File(eacJsonPath).readAsBytes();
|
||||
final Map eacJson = json.decode(utf8.decode(eacJsonData));
|
||||
final eacID = eacJson["productid"];
|
||||
try {
|
||||
var result = await Process.run(
|
||||
"${item.value}\\EasyAntiCheat_EOS_Setup.exe", ["install", eacID]);
|
||||
dPrint("${item.value}\\EasyAntiCheat_EOS_Setup.exe install $eacID");
|
||||
if (result.stderr == "") {
|
||||
showToast(context!, "修复成功,请尝试启动游戏。(若问题无法解决,请使用工具箱的 《重装 EAC》)");
|
||||
checkResult?.remove(item);
|
||||
notifyListeners();
|
||||
} else {
|
||||
showToast(context!, "修复失败,${result.stderr}");
|
||||
}
|
||||
} catch (e) {
|
||||
showToast(context!, "修复失败,$e");
|
||||
}
|
||||
return;
|
||||
case "cn_user_name":
|
||||
showToast(context!, "即将跳转,教程来自互联网,请谨慎操作...");
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
launchUrlString(
|
||||
"https://btfy.eu.org/?q=5L+u5pS5d2luZG93c+eUqOaIt+WQjeS7juS4reaWh+WIsOiLseaWhw==");
|
||||
return;
|
||||
default:
|
||||
showToast(context!, "该问题暂不支持自动处理,请提供截图寻求帮助");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
openDir(rsiLauncherInstalledPath) async {
|
||||
await Process.run(SystemHelper.powershellPath,
|
||||
["explorer.exe", "/select,\"$rsiLauncherInstalledPath\""]);
|
||||
}
|
||||
|
||||
onMenuTap(String key) async {
|
||||
const String gameInstallReqInfo =
|
||||
"该功能需要一个有效的安装位置\n\n如果您的游戏未下载完成,请等待下载完毕后使用此功能。\n\n如果您的游戏已下载完毕但未识别,请启动一次游戏后重新打开盒子 或 在设置选项中手动设置安装位置。";
|
||||
switch (key) {
|
||||
case "auto_check":
|
||||
doCheck()?.call();
|
||||
BaseUIContainer(
|
||||
uiCreate: () => GameDoctorUI(),
|
||||
modelCreate: () => GameDoctorUIModel(scInstalledPath))
|
||||
.push(context!);
|
||||
return;
|
||||
case "localization":
|
||||
if (scInstalledPath == "not_install") {
|
||||
showToast(context!, "该功能需要一个有效的安装位置");
|
||||
showToast(context!, gameInstallReqInfo);
|
||||
return;
|
||||
}
|
||||
await showDialog(
|
||||
@ -413,7 +215,7 @@ class HomeUIModel extends BaseUIModel {
|
||||
return;
|
||||
case "performance":
|
||||
if (scInstalledPath == "not_install") {
|
||||
showToast(context!, "该功能需要一个有效的安装位置");
|
||||
showToast(context!, gameInstallReqInfo);
|
||||
return;
|
||||
}
|
||||
AnalyticsApi.touch("performance_launch");
|
||||
@ -589,7 +391,7 @@ class HomeUIModel extends BaseUIModel {
|
||||
dPrint('stderr: ${result.stderr}');
|
||||
if (result.exitCode != 0) {
|
||||
showToast(context!,
|
||||
"游戏非正常退出\nexitCode=${result.exitCode}\nstdout=${result.stdout}\nstderr=${result.stderr}");
|
||||
"游戏非正常退出\nexitCode=${result.exitCode}\nstdout=${result.stdout}\nstderr=${result.stderr}\n\n诊断信息:");
|
||||
}
|
||||
|
||||
final launchFile = File("$installPath\\loginData.json");
|
||||
|
Reference in New Issue
Block a user