Rewrite game launch code; Fix override_exe; auto renew session
This commit is contained in:
parent
bdbb840e32
commit
8ac9a1c41f
|
@ -1,3 +1,3 @@
|
|||
__version__ = "1.8.7"
|
||||
__version__ = "1.8.8"
|
||||
|
||||
code_name = "Stellula Kakopo"
|
||||
|
|
16
rare/app.py
16
rare/app.py
|
@ -7,8 +7,9 @@ import sys
|
|||
import time
|
||||
import traceback
|
||||
from argparse import Namespace
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtCore import Qt, QThreadPool, QSettings, QTranslator
|
||||
from PyQt5.QtCore import Qt, QThreadPool, QSettings, QTranslator, QTimer
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMessageBox
|
||||
from requests import HTTPError
|
||||
|
@ -159,6 +160,19 @@ class App(QApplication):
|
|||
|
||||
self.launch_dialog.login()
|
||||
|
||||
dt_exp = datetime.fromisoformat(self.core.lgd.userdata['expires_at'][:-1])
|
||||
dt_now = datetime.utcnow()
|
||||
td = abs(dt_exp - dt_now)
|
||||
self.timer = QTimer()
|
||||
self.timer.timeout.connect(self.re_login)
|
||||
self.timer.start(int(td.total_seconds() - 60))
|
||||
|
||||
def re_login(self):
|
||||
logger.info("Session expires shortly. Renew session")
|
||||
self.core.login()
|
||||
self.timer.stop()
|
||||
self.timer.deleteLater()
|
||||
|
||||
def show_mainwindow(self):
|
||||
if self.window_launched:
|
||||
self.mainwindow.show()
|
||||
|
|
|
@ -57,16 +57,15 @@ class GameSettings(DefaultGameSettings):
|
|||
f"{self.game.app_name}/auto_sync_cloud", self.cloud_sync.isChecked()
|
||||
)
|
||||
)
|
||||
self.override_exe_edit.textChanged.connect(
|
||||
lambda text: self.save_line_edit("override_exe", text)
|
||||
)
|
||||
self.launch_params.textChanged.connect(
|
||||
lambda x: self.save_line_edit("start_params", x)
|
||||
)
|
||||
|
||||
self.game_settings_layout.setAlignment(Qt.AlignTop)
|
||||
|
||||
self.linux_settings.mangohud.set_wrapper_activated.connect(
|
||||
lambda active: self.wrapper_settings.add_wrapper("mangohud")
|
||||
if active else self.wrapper_settings.delete_wrapper("mangohud"))
|
||||
|
||||
def compute_save_path(self):
|
||||
if (
|
||||
self.core.is_installed(self.game.app_name)
|
||||
|
|
|
@ -6,14 +6,15 @@ import webbrowser
|
|||
from dataclasses import dataclass
|
||||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import QObject, QSettings, QProcess, QProcessEnvironment, pyqtSignal
|
||||
from PyQt5.QtCore import QObject, QSettings, QProcess, QProcessEnvironment, pyqtSignal, QUrl
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
from PyQt5.QtWidgets import QMessageBox, QPushButton
|
||||
from legendary.models.game import LaunchParameters, InstalledGame
|
||||
|
||||
from legendary.models.game import LaunchParameters
|
||||
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
|
||||
from rare.components.dialogs.uninstall_dialog import UninstallDialog
|
||||
from rare.components.extra.console import ConsoleWindow
|
||||
from rare.components.tabs.games import CloudSaveUtils
|
||||
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
|
||||
from rare.utils import legendary_utils
|
||||
from rare.utils.meta import RareGameMeta
|
||||
|
||||
|
@ -168,156 +169,24 @@ class GameUtils(QObject):
|
|||
)
|
||||
return
|
||||
|
||||
process = GameProcess(app_name)
|
||||
process.setProcessChannelMode(GameProcess.MergedChannels)
|
||||
def _launch_real():
|
||||
process = self._get_process(app_name, env)
|
||||
|
||||
if game.third_party_store != "Origin":
|
||||
if not offline:
|
||||
if not skip_update_check and not self.core.is_noupdate_game(app_name):
|
||||
# check updates
|
||||
try:
|
||||
latest = self.core.get_asset(
|
||||
app_name, igame.platform, update=False
|
||||
)
|
||||
except ValueError:
|
||||
self.finished.emit(app_name, self.tr("Metadata doesn't exist"))
|
||||
return
|
||||
else:
|
||||
if latest.build_version != igame.version:
|
||||
self.finished.emit(app_name, self.tr("Please update game"))
|
||||
return
|
||||
if game.third_party_store != "Origin":
|
||||
self._launch_game(igame, process, offline, skip_update_check, ask_always_sync)
|
||||
else:
|
||||
self._launch_origin(igame.app_name, process)
|
||||
|
||||
params: LaunchParameters = self.core.get_launch_parameters(
|
||||
app_name=app_name, offline=offline, wine_bin=wine_bin, wine_pfx=wine_pfx
|
||||
)
|
||||
|
||||
full_params = list()
|
||||
|
||||
if os.environ.get("container") == "flatpak":
|
||||
full_params.extend(["flatpak-spawn", "--host"])
|
||||
|
||||
full_params.extend(params.launch_command)
|
||||
full_params.append(
|
||||
os.path.join(params.game_directory, params.game_executable)
|
||||
)
|
||||
full_params.extend(params.game_parameters)
|
||||
full_params.extend(params.egl_parameters)
|
||||
full_params.extend(params.user_parameters)
|
||||
|
||||
process.setWorkingDirectory(params.working_directory)
|
||||
environment = QProcessEnvironment()
|
||||
full_env = os.environ.copy()
|
||||
full_env.update(params.environment)
|
||||
for env, value in full_env.items():
|
||||
environment.insert(env, value)
|
||||
|
||||
if platform.system() != "Windows":
|
||||
# wine prefixes
|
||||
for env in ["STEAM_COMPAT_DATA_PATH", "WINEPREFIX"]:
|
||||
if val := full_env.get(env):
|
||||
if not os.path.exists(val):
|
||||
try:
|
||||
os.makedirs(val)
|
||||
except PermissionError as e:
|
||||
logger.error(str(e))
|
||||
QMessageBox.warning(
|
||||
None,
|
||||
"Error",
|
||||
self.tr(
|
||||
"Error while launching {}. No permission to create {} for {}"
|
||||
).format(game.app_title, val, env),
|
||||
)
|
||||
process.deleteLater()
|
||||
return
|
||||
# check wine executable
|
||||
if shutil.which(full_params[0]) is None:
|
||||
# wine binary does not exist
|
||||
QMessageBox.warning(
|
||||
None,
|
||||
"Warning",
|
||||
self.tr(
|
||||
"'{}' does not exist. Please change it in Settings"
|
||||
).format(full_params[0]),
|
||||
)
|
||||
process.deleteLater()
|
||||
return
|
||||
|
||||
if shutil.which(full_params[0]) is None:
|
||||
QMessageBox.warning(None, "Warning", self.tr("'{}' does not exist").format(full_params[0]))
|
||||
env = self.core.get_app_environment(app_name, wine_pfx=wine_pfx)
|
||||
pre_cmd, wait = self.core.get_pre_launch_command(app_name)
|
||||
if pre_cmd:
|
||||
pre_cmd = pre_cmd.split()
|
||||
pre_proc = self._launch_pre_command(env)
|
||||
pre_proc.start(pre_cmd[0], pre_cmd[1:])
|
||||
if wait:
|
||||
pre_proc.finished.connect(_launch_real)
|
||||
return
|
||||
|
||||
process.setProcessEnvironment(environment)
|
||||
process.game_finished.connect(self.game_finished)
|
||||
running_game = RunningGameModel(
|
||||
process=process, app_name=app_name, always_ask_sync=ask_always_sync
|
||||
)
|
||||
|
||||
process.start(full_params[0], full_params[1:])
|
||||
self.game_launched.emit(app_name)
|
||||
self.signals.set_discord_rpc.emit(app_name)
|
||||
logger.info(f"{game.app_title} launched")
|
||||
|
||||
self.running_games[game.app_name] = running_game
|
||||
|
||||
else:
|
||||
origin_uri = self.core.get_origin_uri(game.app_name, self.args.offline)
|
||||
logger.info("Launch Origin Game: ")
|
||||
if platform.system() == "Windows":
|
||||
webbrowser.open(origin_uri)
|
||||
self.finished.emit(app_name, "")
|
||||
return
|
||||
wine_pfx = self.core.lgd.config.get(
|
||||
game.app_name, "wine_prefix", fallback=os.path.expanduser("~/.wine")
|
||||
)
|
||||
if not wine_bin:
|
||||
wine_bin = self.core.lgd.config.get(
|
||||
game.app_name, "wine_executable", fallback="/usr/bin/wine"
|
||||
)
|
||||
|
||||
if shutil.which(wine_bin) is None:
|
||||
# wine binary does not exist
|
||||
QMessageBox.warning(
|
||||
None,
|
||||
"Warning",
|
||||
self.tr(
|
||||
"Wine executable '{}' does not exist. Please change it in Settings"
|
||||
).format(wine_bin),
|
||||
)
|
||||
process.deleteLater()
|
||||
return
|
||||
|
||||
env = self.core.get_app_environment(game.app_name, wine_pfx=wine_pfx)
|
||||
|
||||
if not env.get("WINEPREFIX") and not os.path.exists("/usr/bin/wine"):
|
||||
logger.error(
|
||||
f"In order to launch Origin correctly you must specify the wine binary and prefix "
|
||||
f"to use in the configuration file or command line. See the README for details."
|
||||
)
|
||||
self.finished.emit(
|
||||
app_name,
|
||||
self.tr("No wine executable selected. Please set it in settings"),
|
||||
)
|
||||
return
|
||||
|
||||
environment = QProcessEnvironment()
|
||||
for e in env:
|
||||
environment.insert(e, env[e])
|
||||
process.setProcessEnvironment(environment)
|
||||
process.finished.connect(lambda x: self.game_finished(x, game.app_name))
|
||||
process.start(wine_bin, [origin_uri])
|
||||
|
||||
if QSettings().value("show_console", False, bool):
|
||||
self.console.show()
|
||||
process.readyReadStandardOutput.connect(
|
||||
lambda: self.console.log(
|
||||
str(process.readAllStandardOutput().data(), "utf-8", "ignore")
|
||||
)
|
||||
)
|
||||
process.readyReadStandardError.connect(
|
||||
lambda: self.console.error(
|
||||
str(process.readAllStandardError().data(), "utf-8", "ignore")
|
||||
)
|
||||
)
|
||||
_launch_real()
|
||||
|
||||
def game_finished(self, exit_code, app_name):
|
||||
logger.info(f"Game exited with exit code: {exit_code}")
|
||||
|
@ -370,6 +239,147 @@ class GameUtils(QObject):
|
|||
return
|
||||
self.cloud_save_utils.game_finished(app_name, game.always_ask_sync)
|
||||
|
||||
def _launch_pre_command(self, env: dict):
|
||||
proc = QProcess()
|
||||
proc.setProcessChannelMode(QProcess.MergedChannels)
|
||||
environment = QProcessEnvironment()
|
||||
for e in env:
|
||||
environment.insert(e, env[e])
|
||||
proc.setProcessEnvironment(environment)
|
||||
|
||||
proc.readyReadStandardOutput.connect(
|
||||
lambda: self.console.log(
|
||||
str(proc.readAllStandardOutput().data(), "utf-8", "ignore")
|
||||
)
|
||||
)
|
||||
proc.readyReadStandardError.connect(
|
||||
lambda: self.console.error(
|
||||
str(proc.readAllStandardError().data(), "utf-8", "ignore")
|
||||
)
|
||||
)
|
||||
return proc
|
||||
|
||||
def _get_process(self, app_name, env):
|
||||
process = GameProcess(app_name)
|
||||
process.setProcessChannelMode(GameProcess.MergedChannels)
|
||||
|
||||
environment = QProcessEnvironment()
|
||||
for e in env:
|
||||
environment.insert(e, env[e])
|
||||
process.setProcessEnvironment(environment)
|
||||
|
||||
process.readyReadStandardOutput.connect(
|
||||
lambda: self.console.log(
|
||||
str(process.readAllStandardOutput().data(), "utf-8", "ignore")
|
||||
)
|
||||
)
|
||||
process.readyReadStandardError.connect(
|
||||
lambda: self.console.error(
|
||||
str(process.readAllStandardError().data(), "utf-8", "ignore")
|
||||
)
|
||||
)
|
||||
process.finished.connect(lambda x: self.game_finished(x, app_name))
|
||||
process.stateChanged.connect(
|
||||
lambda state: self.console.show()
|
||||
if (state == QProcess.Running
|
||||
and QSettings().value("show_console", False, bool))
|
||||
else None
|
||||
)
|
||||
return process
|
||||
|
||||
def _launch_origin(self, app_name, process: QProcess):
|
||||
origin_uri = self.core.get_origin_uri(app_name, self.args.offline)
|
||||
logger.info("Launch Origin Game: ")
|
||||
if platform.system() == "Windows":
|
||||
QDesktopServices.openUrl(QUrl(origin_uri))
|
||||
self.finished.emit(app_name, "")
|
||||
return
|
||||
|
||||
command = self.core.get_app_launch_command(app_name)
|
||||
|
||||
if not os.path.exists(command[0]) and shutil.which(command[0]) is None:
|
||||
# wine binary does not exist
|
||||
QMessageBox.warning(
|
||||
None, "Warning",
|
||||
self.tr(
|
||||
"'{}' does not exist. Please change it in Settings"
|
||||
).format(command[0]),
|
||||
)
|
||||
process.deleteLater()
|
||||
return
|
||||
|
||||
process.start(command[0], command[1:])
|
||||
|
||||
def _launch_game(self, igame: InstalledGame, process: QProcess, offline: bool,
|
||||
skip_update_check: bool, ask_always_sync: bool):
|
||||
if not offline: # skip for update
|
||||
if not skip_update_check and not self.core.is_noupdate_game(igame.app_name):
|
||||
# check updates
|
||||
try:
|
||||
latest = self.core.get_asset(
|
||||
igame.app_name, igame.platform, update=False
|
||||
)
|
||||
except ValueError:
|
||||
self.finished.emit(igame.app_name, self.tr("Metadata doesn't exist"))
|
||||
return
|
||||
else:
|
||||
if latest.build_version != igame.version:
|
||||
self.finished.emit(igame.app_name, self.tr("Please update game"))
|
||||
return
|
||||
|
||||
params: LaunchParameters = self.core.get_launch_parameters(
|
||||
app_name=igame.app_name, offline=offline
|
||||
)
|
||||
|
||||
full_params = list()
|
||||
|
||||
if os.environ.get("container") == "flatpak":
|
||||
full_params.extend(["flatpak-spawn", "--host"])
|
||||
|
||||
full_params.extend(params.launch_command)
|
||||
full_params.append(
|
||||
os.path.join(params.game_directory, params.game_executable)
|
||||
)
|
||||
full_params.extend(params.game_parameters)
|
||||
full_params.extend(params.egl_parameters)
|
||||
full_params.extend(params.user_parameters)
|
||||
|
||||
process.setWorkingDirectory(params.working_directory)
|
||||
|
||||
if platform.system() != "Windows":
|
||||
# wine prefixes
|
||||
for env in ["STEAM_COMPAT_DATA_PATH", "WINEPREFIX"]:
|
||||
if val := process.processEnvironment().value(env, ""):
|
||||
if not os.path.exists(val):
|
||||
try:
|
||||
os.makedirs(val)
|
||||
except PermissionError as e:
|
||||
logger.error(str(e))
|
||||
QMessageBox.warning(
|
||||
None,
|
||||
"Error",
|
||||
self.tr(
|
||||
"Error while launching {}. No permission to create {} for {}"
|
||||
).format(igame.title, val, env),
|
||||
)
|
||||
process.deleteLater()
|
||||
return
|
||||
# check wine executable
|
||||
|
||||
if shutil.which(full_params[0]) is None:
|
||||
QMessageBox.warning(None, "Warning", self.tr("'{}' does not exist").format(full_params[0]))
|
||||
return
|
||||
running_game = RunningGameModel(
|
||||
process=process, app_name=igame.app_name, always_ask_sync=ask_always_sync
|
||||
)
|
||||
process.start(full_params[0], full_params[1:])
|
||||
|
||||
self.game_launched.emit(igame.app_name)
|
||||
self.signals.set_discord_rpc.emit(igame.app_name)
|
||||
logger.info(f"{igame.title} launched")
|
||||
|
||||
self.running_games[igame.app_name] = running_game
|
||||
|
||||
def sync_finished(self, app_name):
|
||||
if app_name in self.launch_queue.keys():
|
||||
self.cloud_save_finished.emit(app_name)
|
||||
|
|
Loading…
Reference in a new issue