2023-10-16 23:24:10 +08:00
|
|
|
|
import 'dart:async';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
import 'dart:convert';
|
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
2023-11-28 23:08:34 +08:00
|
|
|
|
import 'package:dart_rss/dart_rss.dart';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
2023-10-28 19:58:26 +08:00
|
|
|
|
import 'package:dio/dio.dart';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
import 'package:hive/hive.dart';
|
2023-10-30 20:39:31 +08:00
|
|
|
|
import 'package:starcitizen_doctor/api/analytics.dart';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
import 'package:starcitizen_doctor/api/api.dart';
|
2023-11-28 23:08:34 +08:00
|
|
|
|
import 'package:starcitizen_doctor/api/rss.dart';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
import 'package:starcitizen_doctor/base/ui_model.dart';
|
2024-01-29 20:44:00 +08:00
|
|
|
|
import 'package:starcitizen_doctor/common/conf/app_conf.dart';
|
|
|
|
|
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
|
|
|
|
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
|
|
|
|
import 'package:starcitizen_doctor/data/app_placard_data.dart';
|
2023-10-28 19:58:26 +08:00
|
|
|
|
import 'package:starcitizen_doctor/data/app_web_localization_versions_data.dart';
|
2023-11-03 00:18:45 +08:00
|
|
|
|
import 'package:starcitizen_doctor/data/countdown_festival_item_data.dart';
|
2023-11-03 23:03:19 +08:00
|
|
|
|
import 'package:starcitizen_doctor/ui/home/countdown/countdown_dialog_ui_model.dart';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
import 'package:starcitizen_doctor/ui/home/dialogs/md_content_dialog_ui.dart';
|
|
|
|
|
import 'package:starcitizen_doctor/ui/home/dialogs/md_content_dialog_ui_model.dart';
|
|
|
|
|
import 'package:starcitizen_doctor/ui/home/localization/localization_ui_model.dart';
|
2023-10-28 18:19:18 +08:00
|
|
|
|
import 'package:starcitizen_doctor/ui/home/login/login_dialog_ui.dart';
|
|
|
|
|
import 'package:starcitizen_doctor/ui/home/login/login_dialog_ui_model.dart';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
import 'package:starcitizen_doctor/ui/home/performance/performance_ui_model.dart';
|
|
|
|
|
import 'package:starcitizen_doctor/ui/home/webview/webview.dart';
|
|
|
|
|
import 'package:starcitizen_doctor/ui/home/webview/webview_localization_capture_ui_model.dart';
|
|
|
|
|
import 'package:url_launcher/url_launcher_string.dart';
|
|
|
|
|
|
2023-11-28 23:08:34 +08:00
|
|
|
|
import 'package:html/parser.dart' as html;
|
2023-12-02 12:35:40 +08:00
|
|
|
|
import 'package:html/dom.dart' as html_dom;
|
2023-12-06 22:45:36 +08:00
|
|
|
|
import 'package:windows_ui/windows_ui.dart';
|
2023-11-28 23:08:34 +08:00
|
|
|
|
|
2023-11-03 23:03:19 +08:00
|
|
|
|
import 'countdown/countdown_dialog_ui.dart';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
import 'localization/localization_ui.dart';
|
|
|
|
|
import 'performance/performance_ui.dart';
|
|
|
|
|
import 'webview/webview_localization_capture_ui.dart';
|
|
|
|
|
|
|
|
|
|
class HomeUIModel extends BaseUIModel {
|
|
|
|
|
var scInstalledPath = "not_install";
|
|
|
|
|
|
|
|
|
|
List<String> scInstallPaths = [];
|
|
|
|
|
|
|
|
|
|
String _lastScreenInfo = "";
|
|
|
|
|
|
|
|
|
|
String get lastScreenInfo => _lastScreenInfo;
|
|
|
|
|
|
|
|
|
|
bool isChecking = false;
|
|
|
|
|
|
|
|
|
|
bool isFixing = false;
|
|
|
|
|
String isFixingString = "";
|
|
|
|
|
|
2023-10-28 18:19:18 +08:00
|
|
|
|
final Map<String, bool> _isGameRunning = {};
|
|
|
|
|
|
|
|
|
|
bool get isCurGameRunning => _isGameRunning[scInstalledPath] ?? false;
|
|
|
|
|
|
2023-11-28 23:08:34 +08:00
|
|
|
|
List<RssItem>? rssVideoItems;
|
|
|
|
|
List<RssItem>? rssTextItems;
|
|
|
|
|
|
2023-10-09 09:32:07 +08:00
|
|
|
|
set lastScreenInfo(String info) {
|
|
|
|
|
_lastScreenInfo = info;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<MapEntry<String, String>>? checkResult;
|
|
|
|
|
|
2023-10-28 19:58:26 +08:00
|
|
|
|
AppWebLocalizationVersionsData? appWebLocalizationVersionsData;
|
|
|
|
|
|
2023-11-03 00:18:45 +08:00
|
|
|
|
List<CountdownFestivalItemData>? countdownFestivalListData;
|
|
|
|
|
|
2023-12-06 22:45:36 +08:00
|
|
|
|
MapEntry<String, bool>? localizationUpdateInfo;
|
|
|
|
|
|
|
|
|
|
bool _isSendLocalizationUpdateNotification = false;
|
|
|
|
|
|
2023-10-09 09:32:07 +08:00
|
|
|
|
final cnExp = RegExp(r"[^\x00-\xff]");
|
|
|
|
|
|
|
|
|
|
AppPlacardData? appPlacardData;
|
|
|
|
|
|
2023-10-16 23:24:10 +08:00
|
|
|
|
List? scServerStatus;
|
|
|
|
|
|
|
|
|
|
Timer? serverUpdateTimer;
|
2023-12-06 22:45:36 +08:00
|
|
|
|
Timer? appUpdateTimer;
|
2023-10-16 23:24:10 +08:00
|
|
|
|
|
|
|
|
|
final statusCnName = const {
|
|
|
|
|
"Platform": "平台",
|
|
|
|
|
"Persistent Universe": "持续宇宙",
|
|
|
|
|
"Electronic Access": "电子访问",
|
2023-10-18 22:34:29 +08:00
|
|
|
|
"Arena Commander": "竞技场指挥官"
|
2023-10-16 23:24:10 +08:00
|
|
|
|
};
|
|
|
|
|
|
2023-11-07 22:49:09 +08:00
|
|
|
|
bool isRsiLauncherStarting = false;
|
|
|
|
|
|
2023-10-09 09:32:07 +08:00
|
|
|
|
@override
|
|
|
|
|
Future loadData() async {
|
|
|
|
|
if (AppConf.networkVersionData == null) return;
|
|
|
|
|
try {
|
|
|
|
|
final r = await Api.getAppPlacard();
|
|
|
|
|
final box = await Hive.openBox("app_conf");
|
|
|
|
|
final version = box.get("close_placard", defaultValue: "");
|
2023-10-16 23:24:10 +08:00
|
|
|
|
if (r.enable == true) {
|
|
|
|
|
if (r.alwaysShow != true && version == r.version) {
|
|
|
|
|
} else {
|
|
|
|
|
appPlacardData = r;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-28 20:37:45 +08:00
|
|
|
|
updateSCServerStatus();
|
|
|
|
|
notifyListeners();
|
2023-10-28 19:58:26 +08:00
|
|
|
|
appWebLocalizationVersionsData = AppWebLocalizationVersionsData.fromJson(
|
2023-10-29 18:02:56 +08:00
|
|
|
|
json.decode((await Api.dio.get(
|
2024-01-29 20:44:00 +08:00
|
|
|
|
"${URLConf.webTranslateHomeUrl}/versions.json",
|
2023-10-29 18:02:56 +08:00
|
|
|
|
options: Options(responseType: ResponseType.plain)))
|
|
|
|
|
.data));
|
2023-11-03 00:18:45 +08:00
|
|
|
|
countdownFestivalListData = await Api.getFestivalCountdownList();
|
|
|
|
|
notifyListeners();
|
2024-02-03 12:28:15 +08:00
|
|
|
|
_loadRRS();
|
2023-10-09 09:32:07 +08:00
|
|
|
|
} catch (e) {
|
|
|
|
|
dPrint(e);
|
|
|
|
|
}
|
2023-12-06 22:45:36 +08:00
|
|
|
|
// check Localization update
|
|
|
|
|
_checkLocalizationUpdate();
|
2023-10-16 23:24:10 +08:00
|
|
|
|
notifyListeners();
|
2023-10-09 09:32:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initModel() {
|
|
|
|
|
reScanPath();
|
2023-10-16 23:24:10 +08:00
|
|
|
|
serverUpdateTimer = Timer.periodic(
|
2023-12-06 22:45:36 +08:00
|
|
|
|
const Duration(minutes: 10),
|
2023-10-16 23:24:10 +08:00
|
|
|
|
(timer) {
|
|
|
|
|
updateSCServerStatus();
|
|
|
|
|
},
|
|
|
|
|
);
|
2023-12-06 22:45:36 +08:00
|
|
|
|
|
|
|
|
|
appUpdateTimer = Timer.periodic(const Duration(minutes: 30), (timer) {
|
|
|
|
|
_checkLocalizationUpdate();
|
|
|
|
|
});
|
2024-02-03 12:28:15 +08:00
|
|
|
|
Future.delayed(const Duration(milliseconds: 100)).then((value) {
|
|
|
|
|
if (URLConf.isUsingFallback) {
|
|
|
|
|
if (!mounted) return;
|
|
|
|
|
showToast(context!, "因源服务器异常(机房故障或遭受攻击),当前正在使用备用线路,可能会出现访问速度下降,敬请谅解。");
|
|
|
|
|
}
|
|
|
|
|
});
|
2023-10-09 09:32:07 +08:00
|
|
|
|
super.initModel();
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-16 23:24:10 +08:00
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
serverUpdateTimer?.cancel();
|
|
|
|
|
serverUpdateTimer = null;
|
2023-12-06 22:45:36 +08:00
|
|
|
|
appUpdateTimer?.cancel();
|
|
|
|
|
appUpdateTimer = null;
|
2023-10-16 23:24:10 +08:00
|
|
|
|
super.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-09 09:32:07 +08:00
|
|
|
|
Future<void> reScanPath() async {
|
|
|
|
|
scInstallPaths.clear();
|
|
|
|
|
scInstalledPath = "not_install";
|
|
|
|
|
lastScreenInfo = "正在扫描 ...";
|
|
|
|
|
try {
|
|
|
|
|
final listData = await SCLoggerHelper.getLauncherLogList();
|
|
|
|
|
if (listData == null) {
|
|
|
|
|
lastScreenInfo = "获取log失败!";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
scInstallPaths = await SCLoggerHelper.getGameInstallPath(listData,
|
2023-11-21 01:05:20 +08:00
|
|
|
|
withVersion: ["LIVE", "PTU", "EPTU"], checkExists: true);
|
2023-10-09 09:32:07 +08:00
|
|
|
|
if (scInstallPaths.isNotEmpty) {
|
|
|
|
|
scInstalledPath = scInstallPaths.first;
|
|
|
|
|
}
|
|
|
|
|
lastScreenInfo = "扫描完毕,共找到 ${scInstallPaths.length} 个有效安装目录";
|
|
|
|
|
} catch (e) {
|
|
|
|
|
lastScreenInfo = "解析 log 文件失败!";
|
2023-10-30 20:39:31 +08:00
|
|
|
|
AnalyticsApi.touch("error_launchLogs");
|
2023-10-09 09:32:07 +08:00
|
|
|
|
showToast(context!,
|
|
|
|
|
"解析 log 文件失败! \n请关闭游戏,退出RSI启动器后重试,若仍有问题,请使用工具箱中的 RSI Launcher log 修复。");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-16 23:24:10 +08:00
|
|
|
|
updateSCServerStatus() async {
|
|
|
|
|
try {
|
|
|
|
|
final s = await Api.getScServerStatus();
|
|
|
|
|
dPrint("updateSCServerStatus===$s");
|
|
|
|
|
scServerStatus = s;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
dPrint(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-28 23:08:34 +08:00
|
|
|
|
Future _loadRRS() async {
|
2024-02-03 12:28:15 +08:00
|
|
|
|
try {
|
|
|
|
|
final v = await RSSApi.getRssVideo();
|
|
|
|
|
rssVideoItems = v;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
final t = await RSSApi.getRssText();
|
|
|
|
|
rssTextItems = t;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
dPrint("RSS update Success !");
|
|
|
|
|
} catch (e) {
|
|
|
|
|
dPrint("_loadRRS Error:$e");
|
|
|
|
|
}
|
2023-11-28 23:08:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-09 09:32:07 +08:00
|
|
|
|
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 =
|
2023-12-05 21:42:54 +08:00
|
|
|
|
"${Platform.environment["appdata"]}\\EasyAntiCheat\\$eacID\\$eacDeploymentId\\anticheatlauncher.log";
|
2023-10-09 09:32:07 +08:00
|
|
|
|
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 {
|
2023-11-13 22:52:40 +08:00
|
|
|
|
await Directory(item.value).create(recursive: true);
|
2023-10-09 09:32:07 +08:00
|
|
|
|
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 {
|
2023-11-13 22:52:40 +08:00
|
|
|
|
var result = await Process.run(
|
|
|
|
|
"${item.value}\\EasyAntiCheat_EOS_Setup.exe", ["install", eacID]);
|
2023-10-09 09:32:07 +08:00
|
|
|
|
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 {
|
2023-11-07 22:35:25 +08:00
|
|
|
|
await Process.run(SystemHelper.powershellPath,
|
2023-10-09 09:32:07 +08:00
|
|
|
|
["explorer.exe", "/select,\"$rsiLauncherInstalledPath\""]);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 22:10:27 +08:00
|
|
|
|
onMenuTap(String key) async {
|
2023-10-09 09:32:07 +08:00
|
|
|
|
switch (key) {
|
|
|
|
|
case "auto_check":
|
|
|
|
|
doCheck()?.call();
|
|
|
|
|
return;
|
|
|
|
|
case "localization":
|
|
|
|
|
if (scInstalledPath == "not_install") {
|
|
|
|
|
showToast(context!, "该功能需要一个有效的安装位置");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-12-06 22:45:36 +08:00
|
|
|
|
await showDialog(
|
2023-10-09 09:32:07 +08:00
|
|
|
|
context: context!,
|
|
|
|
|
dismissWithEsc: false,
|
|
|
|
|
builder: (BuildContext context) {
|
|
|
|
|
return BaseUIContainer(
|
|
|
|
|
uiCreate: () => LocalizationUI(),
|
|
|
|
|
modelCreate: () => LocalizationUIModel(scInstalledPath));
|
|
|
|
|
});
|
2023-12-06 22:45:36 +08:00
|
|
|
|
_checkLocalizationUpdate();
|
2023-10-09 09:32:07 +08:00
|
|
|
|
return;
|
|
|
|
|
case "performance":
|
|
|
|
|
if (scInstalledPath == "not_install") {
|
|
|
|
|
showToast(context!, "该功能需要一个有效的安装位置");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-10-30 20:39:31 +08:00
|
|
|
|
AnalyticsApi.touch("performance_launch");
|
2023-10-09 09:32:07 +08:00
|
|
|
|
BaseUIContainer(
|
|
|
|
|
uiCreate: () => PerformanceUI(),
|
|
|
|
|
modelCreate: () => PerformanceUIModel(scInstalledPath))
|
|
|
|
|
.push(context!);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
showPlacard() {
|
|
|
|
|
switch (appPlacardData?.linkType) {
|
|
|
|
|
case "external":
|
|
|
|
|
launchUrlString(appPlacardData?.link);
|
|
|
|
|
return;
|
|
|
|
|
case "doc":
|
|
|
|
|
showDialog(
|
|
|
|
|
context: context!,
|
|
|
|
|
builder: (context) {
|
|
|
|
|
return BaseUIContainer(
|
|
|
|
|
uiCreate: () => MDContentDialogUI(),
|
|
|
|
|
modelCreate: () => MDContentDialogUIModel(
|
|
|
|
|
appPlacardData?.title ?? "公告详情", appPlacardData?.link));
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
closePlacard() async {
|
|
|
|
|
final box = await Hive.openBox("app_conf");
|
|
|
|
|
await box.put("close_placard", appPlacardData?.version);
|
|
|
|
|
appPlacardData = null;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-28 13:00:10 +08:00
|
|
|
|
goWebView(String title, String url,
|
|
|
|
|
{bool useLocalization = false,
|
|
|
|
|
bool loginMode = false,
|
|
|
|
|
RsiLoginCallback? rsiLoginCallback}) async {
|
2023-10-09 09:32:07 +08:00
|
|
|
|
if (useLocalization) {
|
2023-10-28 13:00:10 +08:00
|
|
|
|
const tipVersion = 2;
|
2023-10-09 09:32:07 +08:00
|
|
|
|
final box = await Hive.openBox("app_conf");
|
|
|
|
|
final skip =
|
|
|
|
|
await box.get("skip_web_localization_tip_version", defaultValue: 0);
|
|
|
|
|
if (skip != tipVersion) {
|
|
|
|
|
final ok = await showConfirmDialogs(
|
|
|
|
|
context!,
|
2023-10-30 20:39:31 +08:00
|
|
|
|
"星际公民网站汉化",
|
2023-10-09 09:32:07 +08:00
|
|
|
|
const Text(
|
2023-10-28 18:19:18 +08:00
|
|
|
|
"本插功能件仅供大致浏览使用,不对任何有关本功能产生的问题负责!在涉及账号操作前请注意确认网站的原本内容!"
|
2023-12-11 23:56:17 +08:00
|
|
|
|
"\n\n\n使用此功能登录账号时请确保您的 SC汉化盒子 是从可信任的来源下载。",
|
2023-10-09 09:32:07 +08:00
|
|
|
|
style: TextStyle(fontSize: 16),
|
|
|
|
|
),
|
|
|
|
|
constraints: BoxConstraints(
|
|
|
|
|
maxWidth: MediaQuery.of(context!).size.width * .6));
|
2023-10-28 13:00:10 +08:00
|
|
|
|
if (!ok) {
|
|
|
|
|
if (loginMode) {
|
|
|
|
|
rsiLoginCallback?.call(null, false);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-10-09 09:32:07 +08:00
|
|
|
|
await box.put("skip_web_localization_tip_version", tipVersion);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!await WebviewWindow.isWebviewAvailable()) {
|
|
|
|
|
showToast(context!, "需要安装 WebView2 Runtime");
|
|
|
|
|
launchUrlString(
|
|
|
|
|
"https://developer.microsoft.com/en-us/microsoft-edge/webview2/");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-10-28 13:00:10 +08:00
|
|
|
|
final webViewModel = WebViewModel(context!,
|
|
|
|
|
loginMode: loginMode, loginCallback: rsiLoginCallback);
|
2023-10-09 09:32:07 +08:00
|
|
|
|
if (useLocalization) {
|
|
|
|
|
isFixingString = "正在初始化汉化资源...";
|
|
|
|
|
isFixing = true;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
try {
|
2023-10-28 19:58:26 +08:00
|
|
|
|
await webViewModel.initLocalization(appWebLocalizationVersionsData!);
|
2023-10-09 09:32:07 +08:00
|
|
|
|
} catch (e) {
|
|
|
|
|
showToast(context!, "初始化网页汉化资源失败!$e");
|
|
|
|
|
}
|
|
|
|
|
isFixingString = "";
|
|
|
|
|
isFixing = false;
|
|
|
|
|
}
|
2023-10-28 13:00:10 +08:00
|
|
|
|
await webViewModel.initWebView(
|
|
|
|
|
title: title,
|
|
|
|
|
);
|
2023-10-09 09:32:07 +08:00
|
|
|
|
if (await File(
|
|
|
|
|
"${AppConf.applicationSupportDir}\\webview_data\\enable_webview_localization_capture")
|
|
|
|
|
.exists()) {
|
|
|
|
|
webViewModel.enableCapture = true;
|
|
|
|
|
BaseUIContainer(
|
|
|
|
|
uiCreate: () => WebviewLocalizationCaptureUI(),
|
|
|
|
|
modelCreate: () =>
|
|
|
|
|
WebviewLocalizationCaptureUIModel(webViewModel))
|
|
|
|
|
.push(context!)
|
|
|
|
|
.then((_) {
|
|
|
|
|
webViewModel.enableCapture = false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-28 20:37:45 +08:00
|
|
|
|
await Future.delayed(const Duration(milliseconds: 500));
|
2023-10-09 09:32:07 +08:00
|
|
|
|
await webViewModel.launch(url);
|
|
|
|
|
notifyListeners();
|
|
|
|
|
}
|
2023-10-16 23:24:10 +08:00
|
|
|
|
|
|
|
|
|
launchRSI() async {
|
2023-10-28 18:19:18 +08:00
|
|
|
|
if (scInstalledPath == "not_install") {
|
|
|
|
|
showToast(context!, "该功能需要一个有效的安装位置");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-11-07 22:49:09 +08:00
|
|
|
|
|
|
|
|
|
if (AppConf.isMSE) {
|
|
|
|
|
if (isCurGameRunning) {
|
|
|
|
|
await Process.run(
|
|
|
|
|
SystemHelper.powershellPath, ["ps \"StarCitizen\" | kill"]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
AnalyticsApi.touch("gameLaunch");
|
|
|
|
|
showDialog(
|
|
|
|
|
context: context!,
|
|
|
|
|
dismissWithEsc: false,
|
|
|
|
|
builder: (context) {
|
|
|
|
|
return BaseUIContainer(
|
|
|
|
|
uiCreate: () => LoginDialog(),
|
|
|
|
|
modelCreate: () => LoginDialogModel(scInstalledPath, this));
|
|
|
|
|
});
|
|
|
|
|
} else {
|
2023-11-16 20:53:27 +08:00
|
|
|
|
final ok = await showConfirmDialogs(
|
|
|
|
|
context!,
|
|
|
|
|
"一键启动功能提示",
|
|
|
|
|
const Text("为确保账户安全,一键启动功能已在开发版中禁用,我们将在微软商店版本中提供此功能。"
|
2023-11-16 20:59:11 +08:00
|
|
|
|
"\n\n微软商店版由微软提供可靠的分发下载与数字签名,可有效防止软件被恶意篡改。\n\n提示:您无需使用盒子启动游戏也可使用汉化。"),
|
2023-11-16 20:53:27 +08:00
|
|
|
|
confirm: "安装微软商店版本",
|
|
|
|
|
cancel: "取消");
|
|
|
|
|
if (ok == true) {
|
|
|
|
|
await launchUrlString(
|
|
|
|
|
"https://apps.microsoft.com/detail/9NF3SWFWNKL1?launch=true");
|
|
|
|
|
await Future.delayed(const Duration(seconds: 2));
|
|
|
|
|
exit(0);
|
2023-11-07 22:49:09 +08:00
|
|
|
|
}
|
2023-10-28 18:19:18 +08:00
|
|
|
|
}
|
2023-10-16 23:24:10 +08:00
|
|
|
|
}
|
2023-10-19 19:44:23 +08:00
|
|
|
|
|
|
|
|
|
bool isRSIServerStatusOK(Map map) {
|
|
|
|
|
return (map["status"] == "ok" || map["status"] == "operational");
|
|
|
|
|
}
|
2023-10-28 18:19:18 +08:00
|
|
|
|
|
2023-11-21 21:25:59 +08:00
|
|
|
|
doLaunchGame(String launchExe, List<String> args, String installPath,
|
2023-11-23 22:30:00 +08:00
|
|
|
|
String? processorAffinity) async {
|
2023-10-28 18:19:18 +08:00
|
|
|
|
_isGameRunning[installPath] = true;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
try {
|
2024-01-29 21:22:20 +08:00
|
|
|
|
late ProcessResult result;
|
2023-11-21 21:25:59 +08:00
|
|
|
|
if (processorAffinity == null) {
|
2024-01-29 21:22:20 +08:00
|
|
|
|
result = await Process.run(launchExe, args);
|
2023-11-21 21:25:59 +08:00
|
|
|
|
} else {
|
2023-11-28 20:04:15 +08:00
|
|
|
|
dPrint("set Affinity === $processorAffinity launchExe === $launchExe");
|
2024-01-29 21:22:20 +08:00
|
|
|
|
result = await Process.run("cmd.exe", [
|
2023-11-21 21:25:59 +08:00
|
|
|
|
'/C',
|
|
|
|
|
'Start',
|
|
|
|
|
'"StarCitizen"',
|
|
|
|
|
'/High',
|
|
|
|
|
'/Affinity',
|
2023-11-23 22:30:00 +08:00
|
|
|
|
processorAffinity,
|
2023-11-21 21:25:59 +08:00
|
|
|
|
launchExe,
|
|
|
|
|
...args
|
|
|
|
|
]);
|
2024-01-29 21:22:20 +08:00
|
|
|
|
}
|
|
|
|
|
dPrint('Exit code: ${result.exitCode}');
|
|
|
|
|
dPrint('stdout: ${result.stdout}');
|
|
|
|
|
dPrint('stderr: ${result.stderr}');
|
|
|
|
|
if (result.exitCode != 0) {
|
|
|
|
|
showToast(context!,
|
|
|
|
|
"游戏非正常退出\nexitCode=${result.exitCode}\nstdout=${result.stdout}\nstderr=${result.stderr}");
|
2023-11-21 21:25:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-28 18:19:18 +08:00
|
|
|
|
final launchFile = File("$installPath\\loginData.json");
|
|
|
|
|
if (await launchFile.exists()) {
|
|
|
|
|
await launchFile.delete();
|
|
|
|
|
}
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
_isGameRunning[installPath] = false;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
}
|
2023-11-03 23:03:19 +08:00
|
|
|
|
|
|
|
|
|
onTapFestival() {
|
|
|
|
|
if (countdownFestivalListData == null) return;
|
|
|
|
|
showDialog(
|
|
|
|
|
context: context!,
|
|
|
|
|
builder: (context) {
|
|
|
|
|
return BaseUIContainer(
|
|
|
|
|
uiCreate: () => CountdownDialogUI(),
|
|
|
|
|
modelCreate: () =>
|
|
|
|
|
CountdownDialogUIModel(countdownFestivalListData!));
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-11-28 23:08:34 +08:00
|
|
|
|
|
|
|
|
|
getRssImage(RssItem item) {
|
|
|
|
|
final h = html.parse(item.description ?? "");
|
|
|
|
|
if (h.body == null) return "";
|
|
|
|
|
for (var node in h.body!.nodes) {
|
2023-12-02 12:35:40 +08:00
|
|
|
|
if (node is html_dom.Element) {
|
2023-11-28 23:08:34 +08:00
|
|
|
|
if (node.localName == "img") {
|
|
|
|
|
return node.attributes["src"]?.trim() ?? "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
}
|
2023-11-30 20:28:03 +08:00
|
|
|
|
|
|
|
|
|
handleTitle(String? title) {
|
|
|
|
|
if (title == null) return "";
|
|
|
|
|
title = title.replaceAll("【", "[ ");
|
|
|
|
|
title = title.replaceAll("】", " ] ");
|
|
|
|
|
return title;
|
|
|
|
|
}
|
2023-12-06 22:45:36 +08:00
|
|
|
|
|
|
|
|
|
Future<void> _checkLocalizationUpdate() async {
|
|
|
|
|
final info = await handleError(
|
|
|
|
|
() => LocalizationUIModel.checkLocalizationUpdates(scInstallPaths));
|
|
|
|
|
dPrint("lUpdateInfo === $info");
|
|
|
|
|
localizationUpdateInfo = info;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
|
|
if (info?.value == true && !_isSendLocalizationUpdateNotification) {
|
|
|
|
|
final toastNotifier =
|
|
|
|
|
ToastNotificationManager.createToastNotifierWithId("SC汉化盒子");
|
|
|
|
|
if (toastNotifier != null) {
|
|
|
|
|
final toastContent = ToastNotificationManager.getTemplateContent(
|
|
|
|
|
ToastTemplateType.toastText02);
|
|
|
|
|
if (toastContent != null) {
|
|
|
|
|
final xmlNodeList = toastContent.getElementsByTagName('text');
|
|
|
|
|
const title = '汉化有新版本!';
|
|
|
|
|
final content = '您在 ${info?.key} 安装的汉化有新版本啦!';
|
|
|
|
|
xmlNodeList.item(0)?.appendChild(toastContent.createTextNode(title));
|
|
|
|
|
xmlNodeList
|
|
|
|
|
.item(1)
|
|
|
|
|
?.appendChild(toastContent.createTextNode(content));
|
|
|
|
|
final toastNotification =
|
|
|
|
|
ToastNotification.createToastNotification(toastContent);
|
|
|
|
|
toastNotifier.show(toastNotification);
|
|
|
|
|
_isSendLocalizationUpdateNotification = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-09 09:32:07 +08:00
|
|
|
|
}
|