From 1677ea762c279b551048772e0b55a115c061341d Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Fri, 15 Dec 2023 16:57:32 +0200 Subject: [PATCH] FetchWorker: Fix issue with missing MacOS assets on MacOS Using `LegendaryCore.get_game_and_dlc_list` with platform `Windows` updated the assets only for the `Windows` builds of the games missing `Win32` and `MacOS` assets on clean installs. This caused Rare to not include MacOS install options on MacOS (duh!). This might also have been the cause that users were unable to launch games, since they where only offered the `Windows` build of the games (big duh!). To fix this, fetch the assets for `Win32` and `MacOS` games before getting the final list of games and dlcs based on the `Windows` platform. In this regard, also re-use the existing options for getting metadata to give the option to the user to include them when updating assets. Also add an option to include Unreal engine assets which until now were fetched unconditionally. * Include Unreal: When the user option is `true` or debugging. Defaults to `false` * Update Win32: When the user option is `true` or debugging. Defaults to `false` * Update MacOS: Force on MacOS, when the option is `true` or debugging on other platforms. Defaults to `true` on MacOS and is disabled, `false` on others Furthermore, respect legendary's `default_platform` config option and set it in the config on new configurations. The new method in our LegendaryCore monkey allows us to use that option in RareGame when doing version checks on not installed games, and not defaulting to `Windows`. Finally, set `install_platform_fallback` to false in a new config to avoid unwanted side-effects. --- rare/components/__init__.py | 2 - .../tabs/games/game_info/game_info.py | 4 +- .../tabs/games/integrations/ubisoft_group.py | 1 - rare/components/tabs/settings/legendary.py | 57 +++++++++++++------ rare/lgndr/core.py | 8 ++- rare/models/game.py | 5 +- rare/shared/rare_core.py | 10 +++- rare/shared/workers/fetch.py | 44 ++++++++++++-- rare/ui/components/tabs/settings/legendary.py | 51 ++++++++++------- rare/ui/components/tabs/settings/legendary.ui | 41 +++++++++---- 10 files changed, 162 insertions(+), 61 deletions(-) diff --git a/rare/components/__init__.py b/rare/components/__init__.py index 8d0e4fd4..684a1133 100644 --- a/rare/components/__init__.py +++ b/rare/components/__init__.py @@ -45,8 +45,6 @@ class Rare(RareApp): self.signals = RareCore.instance().signals() self.core = RareCore.instance().core() - config_helper.init_config_handler(self.core) - lang = self.settings.value("language", self.core.language_code, type=str) self.load_translator(lang) diff --git a/rare/components/tabs/games/game_info/game_info.py b/rare/components/tabs/games/game_info/game_info.py index 0fd1d7d4..44e5b691 100644 --- a/rare/components/tabs/games/game_info/game_info.py +++ b/rare/components/tabs/games/game_info/game_info.py @@ -284,7 +284,9 @@ class GameInfo(QWidget, SideTabContents): ) self.ui.platform.setText( - self.rgame.igame.platform if self.rgame.is_installed and not self.rgame.is_non_asset else "Windows" + self.rgame.igame.platform + if self.rgame.is_installed and not self.rgame.is_non_asset + else self.core.default_platform ) self.ui.lbl_grade.setDisabled( diff --git a/rare/components/tabs/games/integrations/ubisoft_group.py b/rare/components/tabs/games/integrations/ubisoft_group.py index eddd303d..008912be 100644 --- a/rare/components/tabs/games/integrations/ubisoft_group.py +++ b/rare/components/tabs/games/integrations/ubisoft_group.py @@ -74,7 +74,6 @@ class UbiConnectWorker(Worker): def __init__(self, core: LegendaryCore, ubi_account_id, partner_link_id): super(UbiConnectWorker, self).__init__() self.signals = UbiConnectWorker.Signals() - self.setAutoDelete(True) self.core = core self.ubi_account_id = ubi_account_id self.partner_link_id = partner_link_id diff --git a/rare/components/tabs/settings/legendary.py b/rare/components/tabs/settings/legendary.py index ec132120..5559b3df 100644 --- a/rare/components/tabs/settings/legendary.py +++ b/rare/components/tabs/settings/legendary.py @@ -1,7 +1,7 @@ -import platform +import platform as pf import re from logging import getLogger -from typing import Tuple +from typing import Tuple, List from PyQt5.QtCore import QObject, pyqtSignal, QThreadPool, QSettings from PyQt5.QtWidgets import QSizePolicy, QWidget, QFileDialog, QMessageBox @@ -19,14 +19,21 @@ class RefreshGameMetaWorker(Worker): class Signals(QObject): finished = pyqtSignal() - def __init__(self): + def __init__(self, platforms: List[str], include_unreal: bool): super(RefreshGameMetaWorker, self).__init__() self.signals = RefreshGameMetaWorker.Signals() - self.setAutoDelete(True) self.core = LegendaryCoreSingleton() + if platforms: + self.platforms = platforms + else: + self.platforms = ["Windows"] + self.skip_ue = not include_unreal def run_real(self) -> None: - self.core.get_game_and_dlc_list(True, force_refresh=True) + for platform in self.platforms: + self.core.get_game_and_dlc_list( + True, platform=platform, force_refresh=True, skip_ue=self.skip_ue + ) self.signals.finished.emit() @@ -34,7 +41,7 @@ class LegendarySettings(QWidget, Ui_LegendarySettings): def __init__(self, parent=None): super(LegendarySettings, self).__init__(parent=parent) self.setupUi(self) - self.settings = QSettings() + self.settings = QSettings(self) self.core = LegendaryCoreSingleton() @@ -82,20 +89,36 @@ class LegendarySettings(QWidget, Ui_LegendarySettings): ) self.locale_layout.addWidget(self.locale_edit) - self.win32_cb.setChecked(self.settings.value("win32_meta", False, bool)) - self.win32_cb.stateChanged.connect(lambda: self.settings.setValue("win32_meta", self.win32_cb.isChecked())) + self.fetch_win32_check.setChecked(self.settings.value("win32_meta", False, bool)) + self.fetch_win32_check.stateChanged.connect( + lambda: self.settings.setValue("win32_meta", self.fetch_win32_check.isChecked()) + ) - self.mac_cb.setChecked(self.settings.value("mac_meta", platform.system() == "Darwin", bool)) - self.mac_cb.stateChanged.connect(lambda: self.settings.setValue("mac_meta", self.mac_cb.isChecked())) + self.fetch_macos_check.setChecked(self.settings.value("macos_meta", pf.system() == "Darwin", bool)) + self.fetch_macos_check.stateChanged.connect( + lambda: self.settings.setValue("macos_meta", self.fetch_macos_check.isChecked()) + ) + self.fetch_macos_check.setDisabled(pf.system() == "Darwin") - self.refresh_game_meta_btn.clicked.connect(self.refresh_game_meta) + self.fetch_unreal_check.setChecked(self.settings.value("unreal_meta", False, bool)) + self.fetch_unreal_check.stateChanged.connect( + lambda: self.settings.setValue("unreal_meta", self.fetch_unreal_check.isChecked()) + ) - def refresh_game_meta(self): - self.refresh_game_meta_btn.setDisabled(True) - self.refresh_game_meta_btn.setText(self.tr("Loading")) - worker = RefreshGameMetaWorker() - worker.signals.finished.connect(lambda: self.refresh_game_meta_btn.setDisabled(False)) - worker.signals.finished.connect(lambda: self.refresh_game_meta_btn.setText(self.tr("Refresh game meta"))) + self.refresh_metadata_button.clicked.connect(self.refresh_metadata) + # FIXME: Disable the button for now because it interferes with RareCore + self.refresh_metadata_button.setEnabled(False) + self.refresh_metadata_button.setVisible(False) + + def refresh_metadata(self): + self.refresh_metadata_button.setDisabled(True) + platforms = [] + if self.fetch_win32_check.isChecked(): + platforms.append("Win32") + if self.fetch_macos_check.isChecked(): + platforms.append("Mac") + worker = RefreshGameMetaWorker(platforms, self.fetch_unreal_check.isChecked()) + worker.signals.finished.connect(lambda: self.refresh_metadata_button.setDisabled(False)) QThreadPool.globalInstance().start(worker) @staticmethod diff --git a/rare/lgndr/core.py b/rare/lgndr/core.py index 478d3128..ba6eaeb0 100644 --- a/rare/lgndr/core.py +++ b/rare/lgndr/core.py @@ -3,6 +3,7 @@ import json import logging import os from multiprocessing import Queue +from sys import platform as sys_platform from uuid import uuid4 # On Windows the monkeypatching of `run_real` below doesn't work like on Linux @@ -17,8 +18,8 @@ from legendary.models.game import Game, InstalledGame from legendary.models.manifest import ManifestMeta from rare.lgndr.downloader.mp.manager import DLManager -from rare.lgndr.lfs.lgndry import LGDLFS from rare.lgndr.glue.exception import LgndrException, LgndrLogHandler +from rare.lgndr.lfs.lgndry import LGDLFS legendary.core.DLManager = DLManager legendary.core.LGDLFS = LGDLFS @@ -50,6 +51,11 @@ class LegendaryCore(LegendaryCoreReal): return ret return unlock + @property + def default_platform(self) -> str: + os_default = "Mac" if sys_platform == "darwin" else "Windows" + return self.lgd.config.get("Legendary", "default_platform", fallback=os_default) + # skip_sync defaults to false but since Rare is persistent, skip by default # def get_installed_game(self, app_name, skip_sync=True) -> InstalledGame: # return super(LegendaryCore, self).get_installed_game(app_name, skip_sync) diff --git a/rare/models/game.py b/rare/models/game.py index 3d54eeef..0bca4d91 100644 --- a/rare/models/game.py +++ b/rare/models/game.py @@ -166,7 +166,8 @@ class RareGame(RareGameSlim): def update_game(self): self.game = self.core.get_game( - self.app_name, update_meta=False, platform=self.igame.platform if self.igame else "Windows" + self.app_name, update_meta=False, + platform=self.igame.platform if self.igame else self.core.default_platform ) def update_igame(self): @@ -228,7 +229,7 @@ class RareGame(RareGameSlim): if self.igame is not None: return self.game.app_version(self.igame.platform) else: - return self.game.app_version() + return self.game.app_version(self.core.default_platform) @property def has_update(self) -> bool: diff --git a/rare/shared/rare_core.py b/rare/shared/rare_core.py index abc68247..101ace50 100644 --- a/rare/shared/rare_core.py +++ b/rare/shared/rare_core.py @@ -1,5 +1,6 @@ import configparser import os +import platform import time from argparse import Namespace from itertools import chain @@ -25,6 +26,7 @@ from .workers import ( ) from .workers.uninstall import uninstall_game from .workers.worker import QueueWorkerInfo, QueueWorkerState +from rare.utils import config_helper logger = getLogger("RareCore") @@ -54,9 +56,10 @@ class RareCore(QObject): self.args(args) self.signals(init=True) self.core(init=True) + config_helper.init_config_handler(self.__core) self.image_manager(init=True) - self.settings = QSettings() + self.settings = QSettings(self) self.queue_workers: List[QueueWorker] = [] self.queue_threadpool = QThreadPool() @@ -140,6 +143,9 @@ class RareCore(QObject): for section in ["Legendary", "default", "default.env"]: if section not in self.__core.lgd.config.sections(): self.__core.lgd.config.add_section(section) + # Set some platform defaults + self.__core.lgd.config.set("Legendary", "default_platform", self.__core.default_platform) + self.__core.lgd.config.set("Legendary", "install_platform_fallback", False) # workaround if egl sync enabled, but no programdata_path # programdata_path might be unset if logging in through the browser if self.__core.egl_sync_enabled: @@ -357,7 +363,7 @@ class RareCore(QObject): yield game.game @property - def dlcs(self) -> Dict[str, Game]: + def dlcs(self) -> Dict[str, set[RareGame]]: """! RareGames that ARE DLCs themselves """ diff --git a/rare/shared/workers/fetch.py b/rare/shared/workers/fetch.py index 701e1890..3df8189a 100644 --- a/rare/shared/workers/fetch.py +++ b/rare/shared/workers/fetch.py @@ -1,9 +1,10 @@ +import platform import time from argparse import Namespace from enum import IntEnum from logging import getLogger -from PyQt5.QtCore import QObject, pyqtSignal +from PyQt5.QtCore import QObject, pyqtSignal, QSettings from requests.exceptions import ConnectionError, HTTPError from rare.lgndr.core import LegendaryCore @@ -27,19 +28,54 @@ class FetchWorker(Worker): self.signals = FetchWorker.Signals() self.core = core self.args = args + self.settings = QSettings() def run_real(self): # Fetch regular EGL games with assets - self.signals.progress.emit(0, self.signals.tr("Updating game metadata")) start_time = time.time() + + want_unreal = self.settings.value("unreal_meta", False, bool) or self.args.debug + want_win32 = self.settings.value("win32_meta", False, bool) + want_macos = self.settings.value("macos_meta", False, bool) + need_macos = platform.system() == "Darwin" + need_windows = not any([want_win32, want_macos, need_macos, self.args.debug]) + + if want_win32 or self.args.debug: + logger.info( + "Requesting Win32 metadata due to %s, %s Unreal engine", + "settings" if want_win32 else "debug", + "with" if want_unreal else "without" + ) + self.signals.progress.emit(00, self.signals.tr("Updating game metadata for Windows")) + self.core.get_game_and_dlc_list( + update_assets=not self.args.offline, platform="Win32", skip_ue=not want_unreal + ) + + if need_macos or want_macos or self.args.debug: + logger.info( + "Requesting MacOS metadata due to %s, %s Unreal engine", + platform if need_macos else "settings" if want_macos else "debug", + "with" if want_unreal else "without" + ) + self.signals.progress.emit(15, self.signals.tr("Updating game metadata for MacOS")) + self.core.get_game_and_dlc_list( + update_assets=not self.args.offline, platform="Mac", skip_ue=not want_unreal + ) + + if need_windows: + self.signals.progress.emit(00, self.signals.tr("Updating game metadata for Windows")) + logger.info( + "Requesting Windows metadata, %s Unreal engine", + "with" if want_unreal else "without" + ) games, dlc_dict = self.core.get_game_and_dlc_list( - update_assets=not self.args.offline, platform="Windows", skip_ue=False + update_assets=need_windows, platform="Windows", skip_ue=not want_unreal ) logger.debug(f"Games {len(games)}, games with DLCs {len(dlc_dict)}") logger.debug(f"Request games: {time.time() - start_time} seconds") # Fetch non-asset games - self.signals.progress.emit(10, self.signals.tr("Updating non-asset metadata")) + self.signals.progress.emit(30, self.signals.tr("Updating non-asset game metadata")) start_time = time.time() try: na_games, na_dlc_dict = self.core.get_non_asset_library_items(force_refresh=False, skip_ue=False) diff --git a/rare/ui/components/tabs/settings/legendary.py b/rare/ui/components/tabs/settings/legendary.py index 634f5efa..f2e71e87 100644 --- a/rare/ui/components/tabs/settings/legendary.py +++ b/rare/ui/components/tabs/settings/legendary.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'rare/ui/components/tabs/settings/legendary.ui' # -# Created by: PyQt5 UI code generator 5.15.7 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_LegendarySettings(object): def setupUi(self, LegendarySettings): LegendarySettings.setObjectName("LegendarySettings") - LegendarySettings.resize(595, 334) + LegendarySettings.resize(681, 456) LegendarySettings.setWindowTitle("LegendarySettings") self.legendary_layout = QtWidgets.QHBoxLayout(LegendarySettings) self.legendary_layout.setObjectName("legendary_layout") @@ -125,21 +125,30 @@ class Ui_LegendarySettings(object): self.clean_button = QtWidgets.QPushButton(self.cleanup_group) self.clean_button.setObjectName("clean_button") self.cleanup_layout.addWidget(self.clean_button) - self.right_layout.addWidget(self.cleanup_group, 0, QtCore.Qt.AlignTop) - self.meta_group = QtWidgets.QGroupBox(LegendarySettings) - self.meta_group.setObjectName("meta_group") - self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.meta_group) + self.right_layout.addWidget(self.cleanup_group) + self.metadata_group = QtWidgets.QGroupBox(LegendarySettings) + self.metadata_group.setObjectName("metadata_group") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.metadata_group) self.verticalLayout_2.setObjectName("verticalLayout_2") - self.win32_cb = QtWidgets.QCheckBox(self.meta_group) - self.win32_cb.setObjectName("win32_cb") - self.verticalLayout_2.addWidget(self.win32_cb) - self.mac_cb = QtWidgets.QCheckBox(self.meta_group) - self.mac_cb.setObjectName("mac_cb") - self.verticalLayout_2.addWidget(self.mac_cb) - self.refresh_game_meta_btn = QtWidgets.QPushButton(self.meta_group) - self.refresh_game_meta_btn.setObjectName("refresh_game_meta_btn") - self.verticalLayout_2.addWidget(self.refresh_game_meta_btn) - self.right_layout.addWidget(self.meta_group) + self.fetch_win32_check = QtWidgets.QCheckBox(self.metadata_group) + self.fetch_win32_check.setObjectName("fetch_win32_check") + self.verticalLayout_2.addWidget(self.fetch_win32_check) + self.fetch_macos_check = QtWidgets.QCheckBox(self.metadata_group) + self.fetch_macos_check.setObjectName("fetch_macos_check") + self.verticalLayout_2.addWidget(self.fetch_macos_check) + self.fetch_unreal_check = QtWidgets.QCheckBox(self.metadata_group) + self.fetch_unreal_check.setObjectName("fetch_unreal_check") + self.verticalLayout_2.addWidget(self.fetch_unreal_check) + self.metadata_info = QtWidgets.QLabel(self.metadata_group) + font = QtGui.QFont() + font.setItalic(True) + self.metadata_info.setFont(font) + self.metadata_info.setObjectName("metadata_info") + self.verticalLayout_2.addWidget(self.metadata_info) + self.refresh_metadata_button = QtWidgets.QPushButton(self.metadata_group) + self.refresh_metadata_button.setObjectName("refresh_metadata_button") + self.verticalLayout_2.addWidget(self.refresh_metadata_button) + self.right_layout.addWidget(self.metadata_group) spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.right_layout.addItem(spacerItem1) self.legendary_layout.addLayout(self.right_layout) @@ -162,10 +171,12 @@ class Ui_LegendarySettings(object): self.cleanup_group.setTitle(_translate("LegendarySettings", "Cleanup")) self.clean_keep_manifests_button.setText(_translate("LegendarySettings", "Clean, but keep manifests")) self.clean_button.setText(_translate("LegendarySettings", "Remove everything")) - self.meta_group.setTitle(_translate("LegendarySettings", "Game metadata")) - self.win32_cb.setText(_translate("LegendarySettings", "Load 32bit data")) - self.mac_cb.setText(_translate("LegendarySettings", "Load MacOS data")) - self.refresh_game_meta_btn.setText(_translate("LegendarySettings", "Refresh game meta")) + self.metadata_group.setTitle(_translate("LegendarySettings", "Platforms")) + self.fetch_win32_check.setText(_translate("LegendarySettings", "Include Win32 games")) + self.fetch_macos_check.setText(_translate("LegendarySettings", "Include MacOS games")) + self.fetch_unreal_check.setText(_translate("LegendarySettings", "Include Unreal engine")) + self.metadata_info.setText(_translate("LegendarySettings", "Restart Rare to apply")) + self.refresh_metadata_button.setText(_translate("LegendarySettings", "Refresh metadata")) if __name__ == "__main__": diff --git a/rare/ui/components/tabs/settings/legendary.ui b/rare/ui/components/tabs/settings/legendary.ui index a69ef823..b87929da 100644 --- a/rare/ui/components/tabs/settings/legendary.ui +++ b/rare/ui/components/tabs/settings/legendary.ui @@ -6,8 +6,8 @@ 0 0 - 595 - 334 + 681 + 456 @@ -200,7 +200,7 @@ - + Cleanup @@ -227,29 +227,48 @@ - + - Game metadata + Platforms - + - Load 32bit data + Include Win32 games - + - Load MacOS data + Include MacOS games - + - Refresh game meta + Include Unreal engine + + + + + + + + true + + + + Restart Rare to apply + + + + + + + Refresh metadata