mirror of
https://mirror.ghproxy.com/https://github.com/StarCitizenToolBox/app.git
synced 2024-12-23 11:13:46 +08:00
一键诊断:支持闪退诊断
This commit is contained in:
parent
27640ec6b7
commit
3ef5df8a80
@ -113,11 +113,69 @@ class SCLoggerHelper {
|
|||||||
return scInstallPaths;
|
return scInstallPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getGameLogs(String gameDir) {
|
static Future<List<String>?> getGameRunningLogs(String gameDir) async {
|
||||||
return "";
|
final logFile = File("$gameDir/Game.log");
|
||||||
|
if (!await logFile.exists()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return await logFile.readAsLines(
|
||||||
|
encoding: const Utf8Codec(allowMalformed: true));
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getGameExitLogInfo(String logs) {
|
static MapEntry<String, String>? getGameRunningLogInfo(List<String> logs) {
|
||||||
return "";
|
for (var i = logs.length - 1; i > 0; i--) {
|
||||||
|
final line = logs[i];
|
||||||
|
final r = _checkRunningLine(line);
|
||||||
|
if (r != null) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MapEntry<String, String>? _checkRunningLine(String line) {
|
||||||
|
if (line.contains("STATUS_CRYENGINE_OUT_OF_SYSMEM")) {
|
||||||
|
return const MapEntry("可用内存不足", "请尝试增加虚拟内存( 1080p 下, 物理可用+虚拟内存需 > 64G )");
|
||||||
|
}
|
||||||
|
if (line.contains("EXCEPTION_ACCESS_VIOLATION")) {
|
||||||
|
return const MapEntry("游戏触发了最为广泛的崩溃问题,请查看排障指南:",
|
||||||
|
"https://docs.qq.com/doc/DUURxUVhzTmZoY09Z");
|
||||||
|
}
|
||||||
|
if (line.contains("DXGI_ERROR_DEVICE_REMOVED")) {
|
||||||
|
return const MapEntry(
|
||||||
|
"您的显卡崩溃啦!,请查看排障指南:", "https://www.bilibili.com/read/cv19335199");
|
||||||
|
}
|
||||||
|
if (line.contains("Wakeup socket sendto error")) {
|
||||||
|
return const MapEntry("检测到 socket 异常", "如使用 X黑盒 加速器,请尝试更换加速模式");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.contains("The requested operation requires elevated")) {
|
||||||
|
return const MapEntry("权限不足", "请尝试以管理员权限运行启动器,或使用盒子(微软商店版)启动。");
|
||||||
|
}
|
||||||
|
if (line.contains(
|
||||||
|
"The process cannot access the file because is is being used by another process")) {
|
||||||
|
return const MapEntry("游戏进程被占用", "请尝试重启启动器,或直接重启电脑");
|
||||||
|
}
|
||||||
|
if (line.contains("0xc0000043")) {
|
||||||
|
return const MapEntry("游戏程序文件损坏", "请尝试删除 Bin64 文件夹 并在启动器校验。");
|
||||||
|
}
|
||||||
|
if (line.contains("option to verify the content of the Data.p4k file")) {
|
||||||
|
return const MapEntry("P4K文件损坏", "请尝试删除 Data.p4k 文件 并在启动器校验 或 使用盒子分流。");
|
||||||
|
}
|
||||||
|
if (line.contains("OUTOFMEMORY Direct3D could not allocate")) {
|
||||||
|
return const MapEntry("可用显存不足", "请不要在后台运行其他高显卡占用的 游戏/应用,或更换显卡。");
|
||||||
|
}
|
||||||
|
if (line.contains("OUTOFMEMORY Direct3D could not allocate")) {
|
||||||
|
return const MapEntry("可用显存不足", "请不要在后台运行其他高显卡占用的 游戏/应用,或更换显卡。");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unknown
|
||||||
|
if (line.contains("network.replicatedEntityHandle")) {
|
||||||
|
return const MapEntry("_", "network.replicatedEntityHandle");
|
||||||
|
}
|
||||||
|
if (line.contains("Exception Unknown")) {
|
||||||
|
return const MapEntry("_", "Exception Unknown");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,23 +26,19 @@ class GameDoctorUI extends BaseUI<GameDoctorUIModel> {
|
|||||||
))
|
))
|
||||||
else if (model.checkResult == null ||
|
else if (model.checkResult == null ||
|
||||||
model.checkResult!.isEmpty) ...[
|
model.checkResult!.isEmpty) ...[
|
||||||
Expanded(
|
const Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 12),
|
SizedBox(height: 12),
|
||||||
const Text("扫描完毕,没有找到问题!", maxLines: 1),
|
Text("扫描完毕,没有找到问题!", maxLines: 1),
|
||||||
const SizedBox(height: 64),
|
SizedBox(height: 64),
|
||||||
makeRescueBanner(context),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
] else ...[
|
] else
|
||||||
...makeResult(context, model),
|
...makeResult(context, model),
|
||||||
const SizedBox(height: 64),
|
|
||||||
makeRescueBanner(context),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (model.isFixing)
|
if (model.isFixing)
|
||||||
@ -62,6 +58,11 @@ class GameDoctorUI extends BaseUI<GameDoctorUIModel> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: 20,
|
||||||
|
right: 20,
|
||||||
|
child: makeRescueBanner(context),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
@ -70,12 +71,12 @@ class GameDoctorUI extends BaseUI<GameDoctorUIModel> {
|
|||||||
List<Widget> makeResult(BuildContext context, GameDoctorUIModel model) {
|
List<Widget> makeResult(BuildContext context, GameDoctorUIModel model) {
|
||||||
return [
|
return [
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
const Text(
|
|
||||||
"检测结果",
|
|
||||||
style: TextStyle(fontSize: 24),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
Text(model.lastScreenInfo, maxLines: 1),
|
Text(model.lastScreenInfo, maxLines: 1),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
"注意:本工具检测结果仅供参考,若您不理解以下操作,请提供截图给有经验的玩家!",
|
||||||
|
style: TextStyle(color: Colors.red, fontSize: 16),
|
||||||
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
itemCount: model.checkResult!.length,
|
itemCount: model.checkResult!.length,
|
||||||
@ -86,11 +87,7 @@ class GameDoctorUI extends BaseUI<GameDoctorUIModel> {
|
|||||||
return makeResultItem(item, model);
|
return makeResultItem(item, model);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 64),
|
||||||
Text(
|
|
||||||
"注意:本工具检测结果仅供参考,若您不理解以上操作,请提供截图给有经验的玩家!",
|
|
||||||
style: TextStyle(color: Colors.red, fontSize: 16),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +113,7 @@ class GameDoctorUI extends BaseUI<GameDoctorUIModel> {
|
|||||||
children: [
|
children: [
|
||||||
Image.asset("assets/rescue.png", width: 24, height: 24),
|
Image.asset("assets/rescue.png", width: 24, height: 24),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
const Text("需要帮助? 点击加群获得免费人工支援!"),
|
const Text("需要帮助? 点击加群寻求免费人工支援!"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
@ -144,11 +141,31 @@ class GameDoctorUI extends BaseUI<GameDoctorUIModel> {
|
|||||||
"low_ram": MapEntry(
|
"low_ram": MapEntry(
|
||||||
"物理内存过低", "您至少需要 16GB 的物理内存(Memory)才可运行此游戏。(当前大小:${item.value})"),
|
"物理内存过低", "您至少需要 16GB 的物理内存(Memory)才可运行此游戏。(当前大小:${item.value})"),
|
||||||
};
|
};
|
||||||
return ListTile(
|
bool isCheckedError = errorNames.containsKey(item.key);
|
||||||
title: Text(errorNames[item.key]?.key ?? item.key),
|
|
||||||
|
if (isCheckedError) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: FluentTheme.of(context).cardColor,
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(
|
||||||
|
errorNames[item.key]?.key ?? "",
|
||||||
|
style: const TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
subtitle: Padding(
|
subtitle: Padding(
|
||||||
padding: const EdgeInsets.only(top: 4, bottom: 4),
|
padding: const EdgeInsets.only(top: 4, bottom: 4),
|
||||||
child: Text("修复建议: ${errorNames[item.key]?.value ?? "暂无解决方法,请截图反馈"}"),
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
"修复建议: ${errorNames[item.key]?.value ?? "暂无解决方法,请截图反馈"}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14, color: Colors.white.withOpacity(.7)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
trailing: Button(
|
trailing: Button(
|
||||||
onPressed: (errorNames[item.key]?.value == null || model.isFixing)
|
onPressed: (errorNames[item.key]?.value == null || model.isFixing)
|
||||||
@ -163,10 +180,51 @@ class GameDoctorUI extends BaseUI<GameDoctorUIModel> {
|
|||||||
child: Text("修复"),
|
child: Text("修复"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final isSubTitleUrl = item.value.startsWith("https://");
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: FluentTheme.of(context).cardColor,
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(
|
||||||
|
item.key,
|
||||||
|
style: const TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
subtitle: isSubTitleUrl
|
||||||
|
? null
|
||||||
|
: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
item.value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14, color: Colors.white.withOpacity(.7)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: isSubTitleUrl
|
||||||
|
? Button(
|
||||||
|
onPressed: () {
|
||||||
|
launchUrlString(item.value);
|
||||||
|
},
|
||||||
|
child: const Padding(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
|
||||||
|
child: Text("查看解决方案"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getUITitle(BuildContext context, GameDoctorUIModel model) =>
|
String getUITitle(BuildContext context, GameDoctorUIModel model) =>
|
||||||
"一键诊断 -> ${model.scInstalledPath}";
|
"一键诊断 > ${model.scInstalledPath}";
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
@ -48,14 +49,15 @@ class GameDoctorUIModel extends BaseUIModel {
|
|||||||
|
|
||||||
Future _statCheck() async {
|
Future _statCheck() async {
|
||||||
checkResult = [];
|
checkResult = [];
|
||||||
await _checkPreInstall();
|
|
||||||
await _checkEAC();
|
|
||||||
// TODO for debug
|
// TODO for debug
|
||||||
|
|
||||||
// checkResult?.add(const MapEntry("unSupport_system", "android"));
|
// checkResult?.add(const MapEntry("unSupport_system", "android"));
|
||||||
// checkResult?.add(const MapEntry("nvme_PhysicalBytes", "C"));
|
// checkResult?.add(const MapEntry("nvme_PhysicalBytes", "C"));
|
||||||
// checkResult?.add(const MapEntry("no_live_path", ""));
|
// checkResult?.add(const MapEntry("no_live_path", ""));
|
||||||
|
|
||||||
|
await _checkPreInstall();
|
||||||
|
await _checkEAC();
|
||||||
|
await _checkGameRunningLog();
|
||||||
|
|
||||||
if (checkResult!.isEmpty) {
|
if (checkResult!.isEmpty) {
|
||||||
checkResult = null;
|
checkResult = null;
|
||||||
lastScreenInfo = "分析完毕,没有发现问题";
|
lastScreenInfo = "分析完毕,没有发现问题";
|
||||||
@ -68,6 +70,22 @@ class GameDoctorUIModel extends BaseUIModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future _checkGameRunningLog() async {
|
||||||
|
if (scInstalledPath == "not_install") return;
|
||||||
|
lastScreenInfo = "正在检查:Game.log";
|
||||||
|
final logs = await SCLoggerHelper.getGameRunningLogs(scInstalledPath);
|
||||||
|
if (logs == null) return;
|
||||||
|
final info = SCLoggerHelper.getGameRunningLogInfo(logs);
|
||||||
|
if (info != null) {
|
||||||
|
if (info.key != "_") {
|
||||||
|
checkResult?.add(MapEntry("游戏异常退出:${info.key}", info.value));
|
||||||
|
} else {
|
||||||
|
checkResult
|
||||||
|
?.add(MapEntry("游戏异常退出:未知异常", "info:${info.value},请点击右下角加群反馈。"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future _checkEAC() async {
|
Future _checkEAC() async {
|
||||||
if (scInstalledPath == "not_install") return;
|
if (scInstalledPath == "not_install") return;
|
||||||
lastScreenInfo = "正在检查:EAC";
|
lastScreenInfo = "正在检查:EAC";
|
||||||
|
Loading…
Reference in New Issue
Block a user