Add game_utils.py to launch and uninstall games
- All game launch and sync save mechanic is now in game_utils. - some cleanup
This commit is contained in:
parent
a97ea45c7a
commit
65f85f9b3d
|
@ -12,8 +12,10 @@ from rare.ui.components.tabs.games.games_tab import Ui_GamesTab
|
|||
from rare.utils import legendary_utils
|
||||
from rare.utils.extra_widgets import FlowLayout
|
||||
from rare.utils.utils import get_pixmap, download_image
|
||||
from .cloud_save_utils import CloudSaveUtils
|
||||
from .game_info import GameInfoTabs
|
||||
from .game_info.uninstalled_info import UninstalledInfoTabs
|
||||
from .game_utils import GameUtils
|
||||
from .game_widgets.base_installed_widget import BaseInstalledWidget
|
||||
from .game_widgets.base_uninstalled_widget import BaseUninstalledWidget
|
||||
from .game_widgets.installed_icon_widget import InstalledIconWidget
|
||||
|
@ -39,8 +41,7 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
self.core = shared.core
|
||||
self.signals = shared.signals
|
||||
self.settings = QSettings()
|
||||
self.cloud_save_utils = CloudSaveUtils()
|
||||
self.cloud_save_utils.sync_finished.connect(self.sync_finished)
|
||||
self.game_utils = GameUtils(parent=self)
|
||||
self.before_launch_sync = dict()
|
||||
|
||||
self.game_list = shared.api_results.game_list
|
||||
|
@ -54,7 +55,7 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
self.head_bar.egl_sync_clicked.connect(self.show_egl_sync)
|
||||
self.games.layout().insertWidget(0, self.head_bar)
|
||||
|
||||
self.game_info_tabs = GameInfoTabs(self.dlcs, self)
|
||||
self.game_info_tabs = GameInfoTabs(self.dlcs, self.game_utils, self)
|
||||
self.game_info_tabs.back_clicked.connect(lambda: self.setCurrentIndex(0))
|
||||
self.addWidget(self.game_info_tabs)
|
||||
|
||||
|
@ -178,15 +179,6 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
len(self.core.get_installed_list()),
|
||||
len(self.game_list)))
|
||||
|
||||
def sync_finished(self, app_name):
|
||||
self.widgets[app_name][0].info_text = ""
|
||||
self.widgets[app_name][0].info_label.setText("")
|
||||
self.widgets[app_name][1].info_label.setText("")
|
||||
|
||||
if app_name in self.before_launch_sync.keys():
|
||||
self.launch(*self.before_launch_sync[app_name])
|
||||
self.before_launch_sync.pop(app_name)
|
||||
|
||||
def add_installed_widget(self, app_name):
|
||||
pixmap = get_pixmap(app_name)
|
||||
if pixmap.isNull():
|
||||
|
@ -194,26 +186,15 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
download_image(self.core.get_game(app_name), force=True)
|
||||
pixmap = get_pixmap(app_name)
|
||||
|
||||
icon_widget = InstalledIconWidget(app_name, pixmap)
|
||||
icon_widget = InstalledIconWidget(app_name, pixmap, self.game_utils)
|
||||
|
||||
list_widget = InstalledListWidget(app_name, pixmap)
|
||||
list_widget = InstalledListWidget(app_name, pixmap, self.game_utils)
|
||||
|
||||
self.widgets[app_name] = (icon_widget, list_widget)
|
||||
|
||||
icon_widget.show_info.connect(self.show_game_info)
|
||||
list_widget.show_info.connect(self.show_game_info)
|
||||
|
||||
icon_widget.launch_signal.connect(self.prepare_launch)
|
||||
icon_widget.finish_signal.connect(self.finished)
|
||||
|
||||
list_widget.launch_signal.connect(self.prepare_launch)
|
||||
list_widget.finish_signal.connect(self.finished)
|
||||
|
||||
game = self.core.get_game(app_name)
|
||||
if game.supports_cloud_saves:
|
||||
icon_widget.sync_game.connect(self.cloud_save_utils.sync_before_launch_game)
|
||||
list_widget.sync_game.connect(self.cloud_save_utils.sync_before_launch_game)
|
||||
|
||||
if icon_widget.update_available:
|
||||
self.updates.add(app_name)
|
||||
|
||||
|
@ -405,9 +386,6 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
QObjectCleanupHandler().add(self.icon_view.layout())
|
||||
QObjectCleanupHandler().add(self.list_view.layout())
|
||||
|
||||
# QWidget().setLayout(self.icon_view.layout())
|
||||
# QWidget().setLayout(self.list_view.layout())
|
||||
|
||||
self.icon_view.setLayout(icon_layout)
|
||||
self.list_view.setLayout(list_layout)
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ class CloudSaveDialog(QDialog, Ui_SyncSaveDialog):
|
|||
|
||||
|
||||
class CloudSaveUtils(QObject):
|
||||
sync_finished = pyqtSignal(str)
|
||||
sync_finished = pyqtSignal(str, bool)
|
||||
|
||||
def __init__(self):
|
||||
super(CloudSaveUtils, self).__init__()
|
||||
|
@ -119,7 +119,7 @@ class CloudSaveUtils(QObject):
|
|||
|
||||
self.thread_pool = QThreadPool.globalInstance()
|
||||
|
||||
def sync_before_launch_game(self, app_name):
|
||||
def sync_before_launch_game(self, app_name) -> bool:
|
||||
igame = self.core.get_installed_game(app_name)
|
||||
res, (dt_local, dt_remote) = self.core.check_savegame_state(igame.save_path, self.latest_saves.get(app_name))
|
||||
|
||||
|
@ -144,7 +144,6 @@ class CloudSaveUtils(QObject):
|
|||
result = CloudSaveDialog(igame, dt_local, dt_remote, newer).get_action()
|
||||
if result == CloudSaveDialog.UPLOAD:
|
||||
self.upload_saves(igame, dt_local)
|
||||
return
|
||||
elif result == CloudSaveDialog.DOWNLOAD:
|
||||
self.download_saves(igame)
|
||||
elif result == CloudSaveDialog.CANCEL:
|
||||
|
@ -153,20 +152,11 @@ class CloudSaveUtils(QObject):
|
|||
|
||||
return False
|
||||
|
||||
def game_finished(self, app_name, exit_code):
|
||||
def game_finished(self, app_name):
|
||||
igame = self.core.get_installed_game(app_name)
|
||||
res, (dt_local, dt_remote) = self.core.check_savegame_state(igame.save_path, self.latest_saves.get(app_name))
|
||||
|
||||
if res == SaveGameStatus.LOCAL_NEWER:
|
||||
if exit_code != 0:
|
||||
reply = QMessageBox.question(None, "Cloud Saves", self.tr(
|
||||
"Game could crashed. Do you want to upload save files? The save files could be corrupt"),
|
||||
buttons=QMessageBox.Yes | QMessageBox.No, defaultButton=QMessageBox.Yes)
|
||||
|
||||
if reply == QMessageBox.No:
|
||||
self.sync_finished.emit(app_name)
|
||||
return
|
||||
|
||||
self.upload_saves(igame, dt_local)
|
||||
return
|
||||
|
||||
|
@ -202,7 +192,7 @@ class CloudSaveUtils(QObject):
|
|||
|
||||
def worker_finished(self, error_message: str, app_name: str):
|
||||
if not error_message:
|
||||
self.sync_finished.emit(app_name)
|
||||
self.sync_finished.emit(app_name, False)
|
||||
else:
|
||||
QMessageBox.warning(None, "Warning", self.tr("Syncing with cloud failed: \n ") + error_message)
|
||||
self.sync_finished.emit(app_name)
|
||||
self.sync_finished.emit(app_name, True)
|
||||
|
|
|
@ -7,10 +7,11 @@ from rare.utils.extra_widgets import SideTabWidget
|
|||
from .game_dlc import GameDlc
|
||||
from .game_info import GameInfo
|
||||
from .game_settings import GameSettings
|
||||
from ..game_utils import GameUtils
|
||||
|
||||
|
||||
class GameInfoTabs(SideTabWidget):
|
||||
def __init__(self, dlcs: list, parent=None):
|
||||
def __init__(self, dlcs: list, game_utils: GameUtils, parent=None):
|
||||
super(GameInfoTabs, self).__init__(show_back=True, parent=parent)
|
||||
self.core = shared.core
|
||||
self.signals = shared.signals
|
||||
|
@ -22,7 +23,7 @@ class GameInfoTabs(SideTabWidget):
|
|||
self.addTab(self.settings, self.tr("Settings"))
|
||||
|
||||
self.dlc_list = dlcs
|
||||
self.dlc = GameDlc(self.dlc_list, self)
|
||||
self.dlc = GameDlc(self.dlc_list, game_utils, self)
|
||||
self.addTab(self.dlc, self.tr("Downloadable Content"))
|
||||
|
||||
self.tabBar().setCurrentIndex(1)
|
||||
|
|
|
@ -3,10 +3,9 @@ from PyQt5.QtWidgets import QFrame, QWidget, QMessageBox
|
|||
|
||||
from legendary.models.game import Game
|
||||
from rare import shared
|
||||
from rare.components.dialogs.uninstall_dialog import UninstallDialog
|
||||
from rare.components.tabs.games.game_utils import GameUtils
|
||||
from rare.ui.components.tabs.games.game_info.game_dlc import Ui_GameDlc
|
||||
from rare.ui.components.tabs.games.game_info.game_dlc_widget import Ui_GameDlcWidget
|
||||
from rare.utils import legendary_utils
|
||||
from rare.utils.models import InstallOptionsModel
|
||||
from rare.utils.utils import get_pixmap
|
||||
|
||||
|
@ -15,9 +14,10 @@ class GameDlc(QWidget, Ui_GameDlc):
|
|||
install_dlc = pyqtSignal(str, bool)
|
||||
game: Game
|
||||
|
||||
def __init__(self, dlcs: list, parent=None):
|
||||
def __init__(self, dlcs: list, game_utils: GameUtils, parent=None):
|
||||
super(GameDlc, self).__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
self.game_utils = game_utils
|
||||
|
||||
self.available_dlc_scroll.setProperty("noBorder", 1)
|
||||
self.installed_dlc_scroll.setProperty("noBorder", 1)
|
||||
|
@ -62,12 +62,9 @@ class GameDlc(QWidget, Ui_GameDlc):
|
|||
self.available_dlc_label.setVisible(not self.available_dlc_widgets)
|
||||
self.available_dlc_scroll.setVisible(bool(self.available_dlc_widgets))
|
||||
|
||||
def uninstall(self, game):
|
||||
infos = UninstallDialog(game).get_information()
|
||||
if infos == 0:
|
||||
return
|
||||
legendary_utils.uninstall(game.app_name, self.core, infos)
|
||||
self.update_dlcs(self.game.app_name)
|
||||
def uninstall(self, app_name):
|
||||
if self.game_utils.uninstall_game(app_name):
|
||||
self.update_dlcs(app_name)
|
||||
|
||||
def install(self, app_name):
|
||||
if not self.core.is_installed(self.game.app_name):
|
||||
|
@ -80,7 +77,7 @@ class GameDlc(QWidget, Ui_GameDlc):
|
|||
|
||||
class GameDlcWidget(QFrame, Ui_GameDlcWidget):
|
||||
install = pyqtSignal(str) # Appname
|
||||
uninstall = pyqtSignal(Game)
|
||||
uninstall = pyqtSignal(str)
|
||||
|
||||
def __init__(self, dlc: Game, installed: bool, parent=None):
|
||||
super(GameDlcWidget, self).__init__(parent=parent)
|
||||
|
|
199
rare/components/tabs/games/game_utils.py
Normal file
199
rare/components/tabs/games/game_utils.py
Normal file
|
@ -0,0 +1,199 @@
|
|||
import os
|
||||
import platform
|
||||
import webbrowser
|
||||
from dataclasses import dataclass
|
||||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import QObject, QSettings, QProcess, QProcessEnvironment, pyqtSignal
|
||||
from PyQt5.QtWidgets import QMessageBox, QPushButton
|
||||
|
||||
from legendary.models.game import LaunchParameters
|
||||
from rare import shared
|
||||
from rare.components.dialogs.uninstall_dialog import UninstallDialog
|
||||
from rare.components.extra.console import ConsoleWindow
|
||||
from rare.components.tabs.games import CloudSaveUtils
|
||||
from rare.utils import legendary_utils
|
||||
|
||||
logger = getLogger("GameUtils")
|
||||
|
||||
|
||||
class GameProcess(QProcess):
|
||||
game_finished = pyqtSignal(int, str)
|
||||
|
||||
def __init__(self, app_name):
|
||||
super(GameProcess, self).__init__()
|
||||
self.app_name = app_name
|
||||
self.finished.connect(lambda x: self.game_finished.emit(x, self.app_name))
|
||||
|
||||
|
||||
@dataclass
|
||||
class RunningGameModel:
|
||||
process: GameProcess
|
||||
app_name: str
|
||||
|
||||
|
||||
class GameUtils(QObject):
|
||||
running_games = dict()
|
||||
finished = pyqtSignal(str)
|
||||
cloud_save_finished = pyqtSignal(str)
|
||||
launch_queue = dict()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(GameUtils, self).__init__(parent=parent)
|
||||
|
||||
self.core = shared.core
|
||||
self.console = ConsoleWindow()
|
||||
self.cloud_save_utils = CloudSaveUtils()
|
||||
self.cloud_save_utils.sync_finished.connect(self.sync_finished)
|
||||
|
||||
def uninstall_game(self, app_name) -> bool:
|
||||
game = self.core.get_game(app_name)
|
||||
infos = UninstallDialog(game).get_information()
|
||||
if infos == 0:
|
||||
return False
|
||||
legendary_utils.uninstall(game.app_name, self.core, infos)
|
||||
return True
|
||||
|
||||
def prepare_launch(self, app_name, offline: bool = False, skip_update_check: bool = False):
|
||||
game = self.core.get_game(app_name)
|
||||
if game.supports_cloud_saves and self.cloud_save_utils.sync_before_launch_game(app_name):
|
||||
self.launch_queue[app_name] = (app_name, skip_update_check, offline)
|
||||
return
|
||||
else:
|
||||
self.launch_game(app_name, offline, skip_update_check)
|
||||
|
||||
def launch_game(self, app_name: str, offline: bool = False, skip_update_check: bool = False, wine_bin: str = None,
|
||||
wine_pfx: str = None):
|
||||
game = self.core.get_game(app_name)
|
||||
igame = self.core.get_installed_game(app_name)
|
||||
|
||||
if QSettings().value("confirm_start", False, bool):
|
||||
if not QMessageBox.question(None, "Launch", self.tr("Do you want to launch {}").format(self.game.app_title),
|
||||
QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
|
||||
logger.info("Cancel Startup")
|
||||
logger.info("Launching " + game.app_title)
|
||||
|
||||
if game.third_party_store == "Origin":
|
||||
offline = False
|
||||
else:
|
||||
if not game:
|
||||
logger.error("Game not found")
|
||||
return
|
||||
if game.is_dlc:
|
||||
logger.error("Game is dlc")
|
||||
return
|
||||
if not os.path.exists(igame.install_path):
|
||||
logger.error("Game doesn't exist")
|
||||
return
|
||||
|
||||
process = GameProcess(app_name)
|
||||
process.setProcessChannelMode(GameProcess.MergedChannels)
|
||||
|
||||
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, update=True)
|
||||
except ValueError:
|
||||
print("Metadata doesn't exist")
|
||||
return None
|
||||
if latest.build_version != igame.version:
|
||||
print("Please update game")
|
||||
return None
|
||||
|
||||
params: LaunchParameters = self.core.get_launch_parameters(app_name=app_name, offline=offline,
|
||||
wine_bin=wine_bin, wine_pfx=wine_pfx)
|
||||
|
||||
full_params = list()
|
||||
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)
|
||||
process.setProcessEnvironment(environment)
|
||||
process.game_finished.connect(self.game_finished)
|
||||
running_game = RunningGameModel(process=process, app_name=app_name)
|
||||
process.start(full_params[0], full_params[1:])
|
||||
|
||||
self.running_games[game.app_name] = running_game
|
||||
|
||||
else:
|
||||
origin_uri = self.core.get_origin_uri(self.game.app_name, self.offline)
|
||||
logger.info("Launch Origin Game: ")
|
||||
if platform.system() == "Windows":
|
||||
webbrowser.open(origin_uri)
|
||||
return
|
||||
wine_pfx = self.core.lgd.config.get(self.game.app_name, 'wine_prefix',
|
||||
fallback=os.path.expanduser("~/.wine"))
|
||||
if not wine_bin:
|
||||
wine_bin = self.core.lgd.config.get(self.game.app_name, 'wine_executable', fallback="/usr/bin/wine")
|
||||
env = self.core.get_app_environment(self.game.app_name, wine_pfx=wine_pfx)
|
||||
|
||||
if not wine_bin or not env.get('WINEPREFIX'):
|
||||
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.')
|
||||
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(
|
||||
bytes(process.readAllStandardOutput()).decode("utf-8", errors="ignore")))
|
||||
process.readyReadStandardError.connect(lambda: self.console.error(
|
||||
bytes(process.readAllStandardOutput()).decode("utf-8", errors="ignore")))
|
||||
|
||||
else:
|
||||
process.readyReadStandardOutput.connect(
|
||||
lambda: print(bytes(process.readAllStandardOutput()).decode("utf-8", errors="ignore")))
|
||||
process.readyReadStandardError.connect(
|
||||
lambda: print(bytes(process.readAllStandardError()).decode("utf-8", errors="ignore")))
|
||||
|
||||
def game_finished(self, exit_code, app_name):
|
||||
logger.info("Game exited with exit code: " + str(exit_code))
|
||||
if exit_code == 53 and self.is_origin:
|
||||
msg_box = QMessageBox()
|
||||
msg_box.setText(self.tr("Origin is not installed. Do you want to download installer file? "))
|
||||
msg_box.addButton(QPushButton("Download"), QMessageBox.YesRole)
|
||||
msg_box.addButton(QPushButton("Cancel"), QMessageBox.RejectRole)
|
||||
resp = msg_box.exec()
|
||||
# click install button
|
||||
if resp == 0:
|
||||
webbrowser.open("https://www.dm.origin.com/download")
|
||||
|
||||
self.running_games.pop(app_name)
|
||||
self.finished.emit(app_name)
|
||||
|
||||
if QSettings().value("show_console", False, bool):
|
||||
self.console.log(f"Game exited with code: {exit_code}")
|
||||
|
||||
if self.core.get_game(app_name).supports_cloud_saves:
|
||||
if exit_code != 0:
|
||||
r = QMessageBox.question(None, "Question", self.tr(
|
||||
"Game exited with code {}, which is not a normal code. It could be caused by a crash. Do you want to sync cloud saves"),
|
||||
buttons=QMessageBox.Yes | QMessageBox.No, defaultButton=QMessageBox.Yes)
|
||||
if r != QMessageBox.Yes:
|
||||
return
|
||||
self.cloud_save_utils.game_finished(app_name)
|
||||
|
||||
def sync_finished(self, app_name):
|
||||
if app_name in self.launch_queue.keys():
|
||||
self.cloud_save_finished.emit(app_name)
|
||||
params = self.launch_queue[app_name]
|
||||
self.launch_queue.pop(app_name)
|
||||
self.launch_game(*params)
|
||||
else:
|
||||
self.cloud_save_finished.emit(app_name)
|
|
@ -1,18 +1,16 @@
|
|||
import os
|
||||
import platform
|
||||
import webbrowser
|
||||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, Qt, QByteArray, QProcessEnvironment
|
||||
from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, Qt, QByteArray
|
||||
from PyQt5.QtGui import QPixmap
|
||||
from PyQt5.QtWidgets import QGroupBox, QMessageBox, QAction, QLabel, QPushButton
|
||||
from PyQt5.QtWidgets import QGroupBox, QMessageBox, QAction, QLabel
|
||||
|
||||
from legendary.models.game import Game
|
||||
from rare import shared
|
||||
from rare.components.dialogs.uninstall_dialog import UninstallDialog
|
||||
from rare.components.extra.console import ConsoleWindow
|
||||
from rare.utils import legendary_utils, utils
|
||||
from rare.utils import utils
|
||||
from rare.utils.utils import create_desktop_link
|
||||
from rare.components.tabs.games.game_utils import GameUtils
|
||||
|
||||
logger = getLogger("Game")
|
||||
|
||||
|
@ -21,13 +19,14 @@ class BaseInstalledWidget(QGroupBox):
|
|||
launch_signal = pyqtSignal(str, QProcess, list)
|
||||
show_info = pyqtSignal(Game)
|
||||
finish_signal = pyqtSignal(str, int)
|
||||
update_list = pyqtSignal()
|
||||
proc: QProcess()
|
||||
sync_game = pyqtSignal(str)
|
||||
|
||||
def __init__(self, app_name, pixmap: QPixmap):
|
||||
def __init__(self, app_name, pixmap: QPixmap, game_utils: GameUtils):
|
||||
super(BaseInstalledWidget, self).__init__()
|
||||
self.core = shared.core
|
||||
self.game_utils = game_utils
|
||||
self.game_utils.cloud_save_finished.connect(self.sync_finished)
|
||||
self.sync_cloud_saves = False
|
||||
|
||||
self.game = self.core.get_game(app_name)
|
||||
if self.game.third_party_store != "Origin":
|
||||
|
@ -55,7 +54,7 @@ class BaseInstalledWidget(QGroupBox):
|
|||
|
||||
if self.game.supports_cloud_saves:
|
||||
sync = QAction(self.tr("Sync with cloud"), self)
|
||||
sync.triggered.connect(lambda: self.sync_game.emit(self.game.app_name))
|
||||
sync.triggered.connect(self.sync_game)
|
||||
self.addAction(sync)
|
||||
|
||||
if os.path.exists(os.path.expanduser(f"~/Desktop/{self.game.app_title}.desktop")) \
|
||||
|
@ -86,7 +85,9 @@ class BaseInstalledWidget(QGroupBox):
|
|||
self.addAction(reload_image)
|
||||
|
||||
uninstall = QAction(self.tr("Uninstall"), self)
|
||||
uninstall.triggered.connect(self.uninstall)
|
||||
uninstall.triggered.connect(
|
||||
lambda: shared.signals.update_gamelist.emit(self.game.app_name) if self.game_utils.uninstall_game(
|
||||
self.game.app_name) else None)
|
||||
self.addAction(uninstall)
|
||||
|
||||
def reload_image(self):
|
||||
|
@ -128,108 +129,13 @@ class BaseInstalledWidget(QGroupBox):
|
|||
self.create_start_menu.setText(self.tr("Create Start menu link"))
|
||||
|
||||
def launch(self, offline=False, skip_version_check=False):
|
||||
if QSettings().value("confirm_start", False, bool):
|
||||
if not QMessageBox.question(self, "Launch", self.tr("Do you want to launch {}").format(self.game.app_title),
|
||||
QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
|
||||
logger.info("Cancel Startup")
|
||||
return 1
|
||||
logger.info("Launching " + self.game.app_title)
|
||||
if self.is_origin:
|
||||
self.offline = offline = False
|
||||
if offline or self.offline:
|
||||
if not self.igame.can_run_offline:
|
||||
QMessageBox.warning(self, "Offline",
|
||||
self.tr("Game cannot run offline. Please start game in Online mode"))
|
||||
return
|
||||
if not self.is_origin:
|
||||
try:
|
||||
self.proc, params = legendary_utils.launch_game(self.core, self.game.app_name, offline,
|
||||
skip_version_check=skip_version_check)
|
||||
if self.game.supports_cloud_saves:
|
||||
self.sync_cloud_saves = True
|
||||
self.game_utils.launch_game(self.game.app_name, offline=offline, skip_update_check=skip_version_check)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
QMessageBox.warning(self, "Error",
|
||||
str(e))
|
||||
return
|
||||
else:
|
||||
origin_uri = self.core.get_origin_uri(self.game.app_name, self.offline)
|
||||
logger.info("Launch Origin Game: ")
|
||||
if platform.system() == "Windows":
|
||||
webbrowser.open(origin_uri)
|
||||
return
|
||||
wine_pfx = self.core.lgd.config.get(self.game.app_name, 'wine_prefix', fallback=os.path.expanduser("~/.wine"))
|
||||
wine_binary = self.core.lgd.config.get(self.game.app_name, 'wine_executable', fallback="/usr/bin/wine")
|
||||
env = self.core.get_app_environment(self.game.app_name, wine_pfx=wine_pfx)
|
||||
def sync_finished(self, app_name):
|
||||
if app_name == self.game.app_name:
|
||||
self.sync_cloud_saves = False
|
||||
|
||||
if not wine_binary or not env.get('WINEPREFIX'):
|
||||
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.')
|
||||
return
|
||||
|
||||
self.proc = QProcess()
|
||||
self.proc.setProcessChannelMode(QProcess.MergedChannels)
|
||||
# process.setWorkingDirectory()
|
||||
environment = QProcessEnvironment()
|
||||
for e in env:
|
||||
environment.insert(e, env[e])
|
||||
self.proc.setProcessEnvironment(environment)
|
||||
|
||||
params = [wine_binary, origin_uri]
|
||||
|
||||
if not self.proc:
|
||||
logger.error("Could not start process")
|
||||
return 1
|
||||
self.proc.finished.connect(self.finished)
|
||||
|
||||
if self.settings.value("show_console", False, bool):
|
||||
self.console = ConsoleWindow()
|
||||
self.console.show()
|
||||
self.proc.readyReadStandardOutput.connect(lambda: self.console.log(
|
||||
bytes(self.proc.readAllStandardOutput()).decode("utf-8", errors="ignore")))
|
||||
self.proc.readyReadStandardError.connect(lambda: self.console.error(
|
||||
bytes(self.proc.readAllStandardOutput()).decode("utf-8", errors="ignore")))
|
||||
|
||||
else:
|
||||
self.proc.readyReadStandardOutput.connect(self.stdout)
|
||||
self.proc.readyReadStandardError.connect(self.stderr)
|
||||
|
||||
self.launch_signal.emit(self.game.app_name, self.proc, params)
|
||||
# self.game_running = True
|
||||
|
||||
return 0
|
||||
|
||||
def stdout(self):
|
||||
data = self.proc.readAllStandardOutput()
|
||||
stdout = bytes(data).decode("utf-8", errors="ignore")
|
||||
print(stdout)
|
||||
|
||||
def stderr(self):
|
||||
stderr = bytes(self.proc.readAllStandardError()).decode("utf-8", errors="ignore")
|
||||
print(stderr)
|
||||
logger.error(stderr)
|
||||
# QMessageBox.warning(self, "Warning", stderr + "\nSee ~/.cache/rare/logs/")
|
||||
|
||||
def finished(self, exit_code):
|
||||
logger.info("Game exited with exit code: " + str(exit_code))
|
||||
if exit_code == 53 and self.is_origin:
|
||||
msg_box = QMessageBox()
|
||||
msg_box.setText(self.tr("Origin is not installed. Do you want to download installer file? "))
|
||||
msg_box.addButton(QPushButton("Download"), QMessageBox.YesRole)
|
||||
msg_box.addButton(QPushButton("Cancel"), QMessageBox.RejectRole)
|
||||
resp = msg_box.exec()
|
||||
# click install button
|
||||
if resp == 0:
|
||||
webbrowser.open("https://www.dm.origin.com/download")
|
||||
|
||||
self.finish_signal.emit(self.game.app_name, exit_code)
|
||||
self.game_running = False
|
||||
if self.settings.value("show_console", False, bool):
|
||||
self.console.log(f"Game exited with code: {exit_code}")
|
||||
|
||||
def uninstall(self):
|
||||
infos = UninstallDialog(self.game).get_information()
|
||||
if infos == 0:
|
||||
print("Cancel Uninstall")
|
||||
return
|
||||
legendary_utils.uninstall(self.game.app_name, self.core, infos)
|
||||
self.update_list.emit(self.game.app_name)
|
||||
def sync_game(self):
|
||||
self.game_utils.cloud_save_utils.sync_before_launch_game(self.game.app_name)
|
||||
|
|
|
@ -14,8 +14,8 @@ logger = getLogger("GameWidgetInstalled")
|
|||
class InstalledIconWidget(BaseInstalledWidget):
|
||||
update_game = pyqtSignal()
|
||||
|
||||
def __init__(self, app_name, pixmap):
|
||||
super(InstalledIconWidget, self).__init__(app_name, pixmap)
|
||||
def __init__(self, app_name, pixmap, game_utils):
|
||||
super(InstalledIconWidget, self).__init__(app_name, pixmap, game_utils)
|
||||
self.setObjectName("game_widget_icon")
|
||||
|
||||
self.setContextMenuPolicy(Qt.ActionsContextMenu)
|
||||
|
@ -30,6 +30,8 @@ class InstalledIconWidget(BaseInstalledWidget):
|
|||
|
||||
self.layout.addWidget(self.image)
|
||||
|
||||
self.game_utils.finished.connect(self.game_finished)
|
||||
|
||||
self.title_label = QLabel(f"<h4>{self.game.app_title}</h4>")
|
||||
self.title_label.setAutoFillBackground(False)
|
||||
self.title_label.setWordWrap(True)
|
||||
|
@ -97,3 +99,10 @@ class InstalledIconWidget(BaseInstalledWidget):
|
|||
# right
|
||||
elif e.button() == 2:
|
||||
pass # self.showMenu(e)
|
||||
|
||||
def game_finished(self, app_name):
|
||||
if app_name != self.game.app_name:
|
||||
return
|
||||
|
||||
self.info_text = ""
|
||||
self.info_label.setText("")
|
||||
|
|
|
@ -14,8 +14,8 @@ class InstalledListWidget(BaseInstalledWidget):
|
|||
signal = pyqtSignal(str)
|
||||
update_game = pyqtSignal()
|
||||
|
||||
def __init__(self, app_name, pixmap):
|
||||
super(InstalledListWidget, self).__init__(app_name, pixmap)
|
||||
def __init__(self, app_name, pixmap, game_utils):
|
||||
super(InstalledListWidget, self).__init__(app_name, pixmap, game_utils)
|
||||
self.dev = self.game.metadata["developer"]
|
||||
if self.game.third_party_store != "Origin":
|
||||
self.size = self.igame.install_size
|
||||
|
|
|
@ -3,62 +3,16 @@ import platform
|
|||
import shutil
|
||||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import QProcess, QProcessEnvironment, pyqtSignal, QRunnable, QObject, QCoreApplication
|
||||
from PyQt5.QtCore import pyqtSignal, QCoreApplication, QObject, QRunnable
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from legendary.core import LegendaryCore
|
||||
from legendary.models.game import VerifyResult, LaunchParameters
|
||||
from legendary.models.game import VerifyResult
|
||||
from legendary.utils.lfs import validate_files
|
||||
|
||||
logger = getLogger("Legendary Utils")
|
||||
|
||||
|
||||
def launch_game(core, app_name: str, offline: bool = False, skip_version_check: bool = False):
|
||||
game = core.get_installed_game(app_name)
|
||||
if not game:
|
||||
print("Game not found")
|
||||
return None
|
||||
if game.is_dlc:
|
||||
print("Game is dlc")
|
||||
return None
|
||||
if not os.path.exists(game.install_path):
|
||||
print("Game doesn't exist")
|
||||
return None
|
||||
|
||||
if not offline:
|
||||
|
||||
if not skip_version_check and not core.is_noupdate_game(app_name):
|
||||
# check updates
|
||||
try:
|
||||
latest = core.get_asset(app_name, update=True)
|
||||
except ValueError:
|
||||
print("Metadata doesn't exist")
|
||||
return None
|
||||
if latest.build_version != game.version:
|
||||
print("Please update game")
|
||||
return None
|
||||
params: LaunchParameters = core.get_launch_parameters(app_name=app_name, offline=offline)
|
||||
|
||||
full_params = list()
|
||||
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 = QProcess()
|
||||
process.setProcessChannelMode(QProcess.MergedChannels)
|
||||
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)
|
||||
process.setProcessEnvironment(environment)
|
||||
|
||||
return process, full_params
|
||||
|
||||
|
||||
def uninstall(app_name: str, core: LegendaryCore, options=None):
|
||||
if not options:
|
||||
options = {"keep_files": False}
|
||||
|
|
Loading…
Reference in a new issue