2021-04-14 02:56:44 +12:00
|
|
|
import os
|
2021-06-21 07:55:31 +12:00
|
|
|
import platform
|
2021-03-25 23:49:27 +13:00
|
|
|
from logging import getLogger
|
|
|
|
|
2022-04-01 08:29:31 +13:00
|
|
|
from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, QStandardPaths, Qt, QByteArray
|
2021-09-09 05:22:45 +12:00
|
|
|
from PyQt5.QtGui import QPixmap
|
2022-06-20 21:44:57 +12:00
|
|
|
from PyQt5.QtWidgets import QFrame, QMessageBox, QAction
|
2021-03-25 23:49:27 +13:00
|
|
|
|
2021-11-14 11:56:07 +13:00
|
|
|
from rare.components.tabs.games.game_utils import GameUtils
|
2022-06-19 06:45:36 +12:00
|
|
|
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
|
|
|
|
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
|
2022-07-27 02:58:17 +12:00
|
|
|
from rare.utils.misc import create_desktop_link
|
2022-06-20 05:42:49 +12:00
|
|
|
from rare.widgets.image_widget import ImageWidget
|
2021-03-25 23:49:27 +13:00
|
|
|
|
|
|
|
logger = getLogger("Game")
|
|
|
|
|
|
|
|
|
2022-06-20 21:44:57 +12:00
|
|
|
class BaseInstalledWidget(QFrame):
|
2021-10-24 12:47:49 +13:00
|
|
|
launch_signal = pyqtSignal(str, QProcess, list)
|
2021-12-06 08:11:11 +13:00
|
|
|
show_info = pyqtSignal(str)
|
2021-10-24 12:47:49 +13:00
|
|
|
finish_signal = pyqtSignal(str, int)
|
2021-03-25 23:49:27 +13:00
|
|
|
proc: QProcess()
|
|
|
|
|
2021-11-02 10:53:04 +13:00
|
|
|
def __init__(self, app_name, pixmap: QPixmap, game_utils: GameUtils):
|
2021-03-25 23:49:27 +13:00
|
|
|
super(BaseInstalledWidget, self).__init__()
|
2022-02-26 06:43:27 +13:00
|
|
|
self.core = LegendaryCoreSingleton()
|
|
|
|
self.signals = GlobalSignalsSingleton()
|
|
|
|
self.args = ArgumentsSingleton()
|
2022-06-19 06:45:36 +12:00
|
|
|
self.image_manager = ImageManagerSingleton()
|
2021-11-02 10:53:04 +13:00
|
|
|
self.game_utils = game_utils
|
2021-11-04 07:54:04 +13:00
|
|
|
|
2021-11-03 09:57:19 +13:00
|
|
|
self.syncing_cloud_saves = False
|
|
|
|
|
|
|
|
self.texts = {
|
|
|
|
"needs_verification": self.tr("Please verify game before playing"),
|
|
|
|
"hover": {
|
|
|
|
"update_available": self.tr("Start game without version check"),
|
|
|
|
"launch": self.tr("Launch Game"),
|
|
|
|
"launch_origin": self.tr("Launch/Link"),
|
2021-12-24 22:09:50 +13:00
|
|
|
"running": self.tr("Game running"),
|
2022-04-26 09:30:52 +12:00
|
|
|
"launch_offline": self.tr("Launch offline")
|
2021-11-03 09:57:19 +13:00
|
|
|
},
|
|
|
|
"default": {
|
|
|
|
"running": self.tr("Game running"),
|
|
|
|
"syncing": self.tr("Syncing cloud saves"),
|
2021-12-24 22:09:50 +13:00
|
|
|
"update_available": self.tr("Update available"),
|
2022-04-26 09:30:52 +12:00
|
|
|
"no_meta": self.tr("Game is only offline available")
|
2021-12-24 22:09:50 +13:00
|
|
|
},
|
2021-11-03 09:57:19 +13:00
|
|
|
}
|
2021-10-24 12:47:49 +13:00
|
|
|
|
|
|
|
self.game = self.core.get_game(app_name)
|
2021-12-23 06:00:31 +13:00
|
|
|
self.igame = self.core.get_installed_game(app_name) # None if origin
|
2021-10-24 12:47:49 +13:00
|
|
|
|
2022-01-07 12:29:29 +13:00
|
|
|
if self.game.app_title == "Unreal Engine":
|
|
|
|
self.game.app_title = f"{self.game.app_title} {self.game.app_name.split('_')[-1]}"
|
|
|
|
|
2022-04-26 09:30:52 +12:00
|
|
|
self.is_only_offline = False
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.core.get_asset(app_name, platform=self.igame.platform).build_version
|
|
|
|
except ValueError:
|
|
|
|
logger.warning(f"Game {self.game.app_title} has no metadata. Set offline true")
|
|
|
|
self.is_only_offline = True
|
2022-04-27 07:03:43 +12:00
|
|
|
except AttributeError:
|
|
|
|
pass
|
2022-04-26 09:30:52 +12:00
|
|
|
|
2022-06-20 05:42:49 +12:00
|
|
|
self.image = ImageWidget(self)
|
|
|
|
self.image.setFixedSize(ImageSize.Display)
|
|
|
|
self.image.setPixmap(pixmap)
|
2021-03-25 23:49:27 +13:00
|
|
|
self.game_running = False
|
2022-02-26 06:43:27 +13:00
|
|
|
self.offline = self.args.offline
|
2021-12-04 13:11:20 +13:00
|
|
|
self.update_available = False
|
2021-12-20 08:32:26 +13:00
|
|
|
if self.igame and self.core.lgd.assets:
|
2021-12-12 13:41:38 +13:00
|
|
|
try:
|
2021-12-24 22:09:50 +13:00
|
|
|
remote_version = self.core.get_asset(
|
|
|
|
self.game.app_name, platform=self.igame.platform, update=False
|
|
|
|
).build_version
|
2021-12-12 13:41:38 +13:00
|
|
|
except ValueError:
|
2022-02-02 10:29:34 +13:00
|
|
|
logger.error(f"Asset error for {self.game.app_title}")
|
2021-12-12 13:41:38 +13:00
|
|
|
self.update_available = False
|
|
|
|
else:
|
|
|
|
if remote_version != self.igame.version:
|
|
|
|
self.update_available = True
|
2021-12-04 13:11:20 +13:00
|
|
|
|
2021-06-30 10:03:03 +12:00
|
|
|
self.data = QByteArray()
|
2021-08-14 08:51:36 +12:00
|
|
|
self.settings = QSettings()
|
2021-03-27 01:29:26 +13:00
|
|
|
|
2021-04-14 02:56:44 +12:00
|
|
|
self.setContextMenuPolicy(Qt.ActionsContextMenu)
|
|
|
|
launch = QAction(self.tr("Launch"), self)
|
|
|
|
launch.triggered.connect(self.launch)
|
|
|
|
self.addAction(launch)
|
|
|
|
|
2021-10-24 12:47:49 +13:00
|
|
|
if self.game.supports_cloud_saves:
|
|
|
|
sync = QAction(self.tr("Sync with cloud"), self)
|
2021-11-02 10:53:04 +13:00
|
|
|
sync.triggered.connect(self.sync_game)
|
2021-10-24 12:47:49 +13:00
|
|
|
self.addAction(sync)
|
|
|
|
|
2022-04-01 08:29:31 +13:00
|
|
|
desktop = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation)
|
2021-12-24 22:09:50 +13:00
|
|
|
if os.path.exists(
|
2022-04-01 08:29:31 +13:00
|
|
|
os.path.join(desktop, f"{self.game.app_title}.desktop")
|
|
|
|
) or os.path.exists(os.path.join(desktop, f"{self.game.app_title}.lnk")):
|
2021-04-14 05:08:49 +12:00
|
|
|
self.create_desktop = QAction(self.tr("Remove Desktop link"))
|
|
|
|
else:
|
|
|
|
self.create_desktop = QAction(self.tr("Create Desktop link"))
|
2021-12-23 06:00:31 +13:00
|
|
|
if self.igame:
|
2021-12-24 22:09:50 +13:00
|
|
|
self.create_desktop.triggered.connect(
|
|
|
|
lambda: self.create_desktop_link("desktop")
|
|
|
|
)
|
2021-09-19 02:34:43 +12:00
|
|
|
self.addAction(self.create_desktop)
|
2021-04-14 05:08:49 +12:00
|
|
|
|
2022-04-01 08:29:31 +13:00
|
|
|
applications = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation)
|
2021-06-21 07:55:31 +12:00
|
|
|
if platform.system() == "Linux":
|
2022-04-01 08:29:31 +13:00
|
|
|
start_menu_file = os.path.join(applications, f"{self.game.app_title}.desktop")
|
2021-06-21 07:55:31 +12:00
|
|
|
elif platform.system() == "Windows":
|
2022-04-01 08:29:31 +13:00
|
|
|
start_menu_file = os.path.join(applications, "..", f"{self.game.app_title}.lnk")
|
2021-05-12 03:29:35 +12:00
|
|
|
else:
|
|
|
|
start_menu_file = ""
|
2021-11-22 10:44:45 +13:00
|
|
|
if platform.system() in ["Windows", "Linux"]:
|
|
|
|
if os.path.exists(start_menu_file):
|
|
|
|
self.create_start_menu = QAction(self.tr("Remove start menu link"))
|
|
|
|
else:
|
|
|
|
self.create_start_menu = QAction(self.tr("Create start menu link"))
|
2021-12-23 06:00:31 +13:00
|
|
|
if self.igame:
|
2021-12-24 22:09:50 +13:00
|
|
|
self.create_start_menu.triggered.connect(
|
|
|
|
lambda: self.create_desktop_link("start_menu")
|
|
|
|
)
|
2021-11-22 10:44:45 +13:00
|
|
|
self.addAction(self.create_start_menu)
|
2021-04-14 02:56:44 +12:00
|
|
|
|
2021-09-09 05:22:45 +12:00
|
|
|
reload_image = QAction(self.tr("Reload Image"), self)
|
|
|
|
reload_image.triggered.connect(self.reload_image)
|
|
|
|
self.addAction(reload_image)
|
|
|
|
|
2022-04-24 03:03:03 +12:00
|
|
|
if self.igame is not None:
|
|
|
|
uninstall = QAction(self.tr("Uninstall"), self)
|
|
|
|
self.addAction(uninstall)
|
|
|
|
uninstall.triggered.connect(
|
|
|
|
lambda: self.signals.update_gamelist.emit([self.game.app_name])
|
|
|
|
if self.game_utils.uninstall_game(self.game.app_name)
|
|
|
|
else None
|
|
|
|
)
|
2021-04-14 02:56:44 +12:00
|
|
|
|
2021-09-09 05:22:45 +12:00
|
|
|
def reload_image(self):
|
2022-06-19 06:45:36 +12:00
|
|
|
self.image_manager.download_image_blocking(self.game, force=True)
|
|
|
|
pm = self.image_manager.get_pixmap(self.game.app_name, color=True)
|
2022-06-20 05:42:49 +12:00
|
|
|
self.image.setPixmap(pm)
|
2021-09-09 05:22:45 +12:00
|
|
|
|
2021-04-14 04:01:25 +12:00
|
|
|
def create_desktop_link(self, type_of_link):
|
2022-04-01 08:29:31 +13:00
|
|
|
if type_of_link == "desktop":
|
|
|
|
shortcut_path = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation)
|
|
|
|
elif type_of_link == "start_menu":
|
|
|
|
shortcut_path = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation)
|
|
|
|
else:
|
|
|
|
return
|
|
|
|
|
|
|
|
if platform.system() == "Windows":
|
|
|
|
shortcut_path = os.path.join(shortcut_path, f"{self.game.app_title}.lnk")
|
|
|
|
elif platform.system() == "Linux":
|
|
|
|
shortcut_path = os.path.join(shortcut_path, f"{self.game.app_title}.desktop")
|
|
|
|
else:
|
2021-12-24 22:09:50 +13:00
|
|
|
QMessageBox.warning(
|
|
|
|
self,
|
|
|
|
"Warning",
|
|
|
|
f"Create a Desktop link is currently not supported on {platform.system()}",
|
|
|
|
)
|
2021-06-21 08:03:14 +12:00
|
|
|
return
|
2022-04-01 08:29:31 +13:00
|
|
|
|
|
|
|
if not os.path.exists(shortcut_path):
|
2021-09-19 02:34:43 +12:00
|
|
|
try:
|
|
|
|
if not create_desktop_link(self.game.app_name, self.core, type_of_link):
|
|
|
|
return
|
|
|
|
except PermissionError:
|
2021-12-24 22:09:50 +13:00
|
|
|
QMessageBox.warning(
|
|
|
|
self, "Error", "Permission error. Cannot create Desktop Link"
|
|
|
|
)
|
2021-04-14 04:01:25 +12:00
|
|
|
if type_of_link == "desktop":
|
|
|
|
self.create_desktop.setText(self.tr("Remove Desktop link"))
|
|
|
|
elif type_of_link == "start_menu":
|
|
|
|
self.create_start_menu.setText(self.tr("Remove Start menu link"))
|
|
|
|
else:
|
2022-04-01 08:29:31 +13:00
|
|
|
if os.path.exists(shortcut_path):
|
|
|
|
os.remove(shortcut_path)
|
2021-04-14 04:01:25 +12:00
|
|
|
|
|
|
|
if type_of_link == "desktop":
|
|
|
|
self.create_desktop.setText(self.tr("Create Desktop link"))
|
|
|
|
elif type_of_link == "start_menu":
|
|
|
|
self.create_start_menu.setText(self.tr("Create Start menu link"))
|
2021-04-14 02:56:44 +12:00
|
|
|
|
2021-03-25 23:49:27 +13:00
|
|
|
def launch(self, offline=False, skip_version_check=False):
|
2022-04-26 09:30:52 +12:00
|
|
|
if self.game_running:
|
|
|
|
return
|
|
|
|
offline = offline or self.is_only_offline
|
|
|
|
if self.is_only_offline and not self.igame.can_run_offline:
|
|
|
|
QMessageBox.warning(self, "Warning",
|
|
|
|
self.tr("This game is probably not in your library and it cannot be launched offline"))
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.game.supports_cloud_saves and not offline:
|
|
|
|
self.syncing_cloud_saves = True
|
|
|
|
self.game_utils.prepare_launch(
|
|
|
|
self.game.app_name, offline, skip_version_check
|
|
|
|
)
|
2021-08-14 08:51:36 +12:00
|
|
|
|
2021-11-02 10:53:04 +13:00
|
|
|
def sync_finished(self, app_name):
|
2021-11-04 07:54:04 +13:00
|
|
|
self.syncing_cloud_saves = False
|
2021-04-13 21:15:14 +12:00
|
|
|
|
2021-11-02 10:53:04 +13:00
|
|
|
def sync_game(self):
|
2022-01-19 09:28:25 +13:00
|
|
|
try:
|
|
|
|
sync = self.game_utils.cloud_save_utils.sync_before_launch_game(
|
|
|
|
self.game.app_name, True
|
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
sync = False
|
|
|
|
if sync:
|
2021-11-03 09:57:19 +13:00
|
|
|
self.syncing_cloud_saves = True
|
|
|
|
|
|
|
|
def game_finished(self, app_name, error):
|
|
|
|
if error:
|
|
|
|
QMessageBox.warning(self, "Error", error)
|
2021-11-14 11:56:07 +13:00
|
|
|
self.game_running = False
|
2022-06-22 11:38:04 +12:00
|
|
|
|
|
|
|
# From RareGame, added from sorting to work
|
|
|
|
@property
|
|
|
|
def is_non_asset(self) -> bool:
|
|
|
|
"""!
|
|
|
|
@brief Property to report if a Game doesn't have assets
|
|
|
|
|
|
|
|
Typically, games have assets, however some games that require
|
|
|
|
other launchers do not have them. Rare treats these games as installed
|
|
|
|
offering to execute their launcher.
|
|
|
|
|
|
|
|
@return bool If the game doesn't have assets
|
|
|
|
"""
|
|
|
|
return not self.game.asset_infos
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_installed(self) -> bool:
|
|
|
|
"""!
|
|
|
|
@brief Property to report if a game is installed
|
|
|
|
|
|
|
|
This returns True if InstalledGame data have been loaded for the game
|
|
|
|
or if the game is a game without assets, for example an Origin game.
|
|
|
|
|
|
|
|
@return bool If the game should be considered installed
|
|
|
|
"""
|
|
|
|
return (self.igame is not None) or self.is_non_asset
|
|
|
|
|
|
|
|
@property
|
|
|
|
def app_title(self) -> str:
|
|
|
|
return self.igame.title if self.igame is not None else self.game.app_title
|