From 07d5e0d2d16835c74b9cd0ee0cc935234384282d Mon Sep 17 00:00:00 2001 From: wyyadd Date: Sun, 5 Nov 2023 08:34:46 +0800 Subject: [PATCH] feat: support non-steam games & add set SteamPath function at setting page --- lib/main.dart | 6 ++-- lib/util/game_launcher.dart | 52 ++++++++++++++++++++------- lib/util/storage.dart | 3 +- lib/widget/custom_setting_dialog.dart | 51 +++++++++++++++++++++++++- pubspec.lock | 16 +++++++++ pubspec.yaml | 1 + 6 files changed, 112 insertions(+), 17 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 18aad4a..6bf694d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'page/game.dart'; import 'page/library.dart'; import 'util/dto.dart'; +import 'util/game_launcher.dart'; import 'util/language.dart'; import 'util/server.dart'; import 'util/storage.dart'; @@ -113,8 +114,6 @@ class _MyHomePageState extends State with WindowListener, SingleTick Tab(text: getTranslatedText('Game', '游戏')), ]; }); - // save config - localStorage.writeConfig(); } void updateBackButton() { @@ -126,10 +125,11 @@ class _MyHomePageState extends State with WindowListener, SingleTick void _loadConfig() async { Map? config = await localStorage.readConfig(); if (config != null) { - String language = config['language'] as String; + String language = config['language'] ?? languageOptions[0]; if (language != selectedLanguage && languageOptions.indexWhere((element) => element == language) != -1) { updateLanguage(language); } + customSteamPath = config['custom_steam_path'] ?? ""; } latestVersion = await server.getLatestVersion(); } diff --git a/lib/util/game_launcher.dart b/lib/util/game_launcher.dart index 0eec69e..4e46a8d 100644 --- a/lib/util/game_launcher.dart +++ b/lib/util/game_launcher.dart @@ -2,15 +2,32 @@ import 'dart:io'; import 'language.dart'; import 'package:flutter/material.dart'; +String customSteamPath = ""; + Future launchGame(String trainerPath, int appId, VoidCallback voidCallback) async { if (Platform.isLinux) { String home = Platform.environment['HOME']!; - String steamPath = '$home/.local/share/Steam'; - await _validateDir(steamPath); + String steamPath = customSteamPath.isEmpty ? '$home/.local/share/Steam' : customSteamPath; + if (!await _dirExist('$steamPath/steamapps')) { + throw Exception( + getTranslatedText( + "Steam path not found.\n\nYou can specify the path in the settings.\n\nCurrent Path: $steamPath\n\nDefault Path: $home/.local/share/Steam", + "未找到Steam路径。\n\n您可以在设置中指定路径。\n\n当前路径为: $steamPath\n\n默认路径为: $home/.local/share/Steam"), + ); + } String gamePath = '$steamPath/steamapps/compatdata/$appId'; - await _validateDir(gamePath); - String protonPath = await _getProtonPath(gamePath); + if (!await _dirExist(gamePath)) { + int? nonSteamGameId = _getAppIdFromPS(); + if (nonSteamGameId == null) { + throw Exception(getTranslatedText( + "Game path not found. Please ensure the game is installed.\n\nIf it's a non-Steam game, make sure the game is running.\n\nCurrent Path: $gamePath", + "游戏路径未找到。请确保游戏已安装。\n\n如果是非Steam游戏,请确保游戏已启动。\n\n当前路径为: $gamePath")); + } else { + gamePath = '$steamPath/steamapps/compatdata/$nonSteamGameId'; + } + } + String protonPath = await _getProtonPath(gamePath); voidCallback(); await Process.run(protonPath, [ 'run', @@ -37,17 +54,28 @@ Future _getProtonPath(String gamePath) async { protonPath = arr.sublist(0, index + 1).join('/'); } } - if (protonPath == null) { - throw Exception('proton not exist'); + if (protonPath == null || !await _dirExist(protonPath)) { + throw Exception(getTranslatedText( + 'Proton path not found.\n\nPlease ensure Proton is installed.\n\nCurrent Path: $protonPath', '未找到Proton路径。\n\n请确认已安装Proton。\n\n当前路径为: $protonPath')); } - _validateDir(protonPath); return '$protonPath/proton'; } -Future _validateDir(String path) async { - var dir = Directory(path); - bool exist = await dir.exists(); - if (!exist) { - throw Exception(getTranslatedText('$path not found\nMake sure Steam, Proton and Game all are installed', '未找到$path\n请确保Steam, Proton和游戏都已安装')); +int? _getAppIdFromPS() { + final processResult = Process.runSync('/bin/sh', ['-c', r'ps aux | grep "SteamLaunch AppId="']); + if (processResult.exitCode == 0) { + final outputLines = (processResult.stdout as String).split('\n'); + for (final line in outputLines) { + final match = RegExp(r'SteamLaunch AppId=(\d+)').firstMatch(line); + final appId = int.tryParse(match?.group(1) ?? ''); + if (appId != null) { + return appId; + } + } } + return null; +} + +Future _dirExist(String path) async { + return await Directory(path).exists(); } diff --git a/lib/util/storage.dart b/lib/util/storage.dart index 7067710..f4c30e9 100644 --- a/lib/util/storage.dart +++ b/lib/util/storage.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'dto.dart'; import 'dart:convert'; import 'file_system.dart'; +import 'game_launcher.dart'; import 'language.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; @@ -65,7 +66,7 @@ class LocalStorage { Future writeConfig() async { final path = await localPath; final file = File('$path/$configFileName'); - String config = jsonEncode({'language': selectedLanguage}); + String config = jsonEncode({'language': selectedLanguage, 'custom_steam_path': customSteamPath}); file.writeAsString(config); } diff --git a/lib/widget/custom_setting_dialog.dart b/lib/widget/custom_setting_dialog.dart index 14b10d2..15ca857 100644 --- a/lib/widget/custom_setting_dialog.dart +++ b/lib/widget/custom_setting_dialog.dart @@ -1,8 +1,10 @@ import '../util/language.dart'; import '../util/storage.dart'; +import '../util/game_launcher.dart'; import 'package:flutter/material.dart'; -import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:package_info_plus/package_info_plus.dart'; class CustomSettingDialog extends StatefulWidget { const CustomSettingDialog({super.key, required this.updateLanguage, required this.latestVersion}); @@ -85,6 +87,7 @@ class _CustomSettingDialogState extends State { setState(() { widget.updateLanguage(newValue); }); + localStorage.writeConfig(); } }, items: languageOptions.map>((String value) { @@ -139,6 +142,52 @@ class _CustomSettingDialogState extends State { ], ), const Divider(height: 5), + Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Text( + getTranslatedText('Set Steam Path', '设置Steam路径'), + style: const TextStyle(fontSize: 20), + ), + ), + const Spacer(), + Padding( + padding: const EdgeInsets.only(right: 20), + child: SizedBox( + width: 100, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF00D3C4), + ), + child: Text(getTranslatedText('Set', '设置')), + onPressed: () { + FilePicker.platform.getDirectoryPath().then((selectedDirectory) { + debugPrint('$selectedDirectory'); + if (selectedDirectory != null) { + customSteamPath = selectedDirectory; + localStorage.writeConfig(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + backgroundColor: const Color(0xFF2E3466), + content: Center( + child: Text( + getTranslatedText('Steam path set successfully', 'Steam路径设置成功'), + style: const TextStyle(color: Colors.white), + ), + ), + ), + ); + } + Navigator.of(context).pop(); + }); + }, + ), + ), + ), + ], + ), + const Divider(height: 5), Row( children: [ Padding( diff --git a/pubspec.lock b/pubspec.lock index 15ad223..18dd047 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -97,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + file_picker: + dependency: "direct main" + description: + name: file_picker + sha256: "4e42aacde3b993c5947467ab640882c56947d9d27342a5b6f2895b23956954a6" + url: "https://pub.dev" + source: hosted + version: "6.1.1" flutter: dependency: "direct main" description: flutter @@ -119,6 +127,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da + url: "https://pub.dev" + source: hosted + version: "2.0.17" flutter_test: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 525ae19..8f34816 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: flutter_cache_manager: ^3.3.1 url_launcher: ^6.1.14 package_info_plus: ^4.2.0 + file_picker: ^6.1.1 dependency_overrides: flutter_cache_manager: