1
0
Fork 0
mirror of synced 2024-06-02 02:34:40 +12:00

RareCore: Add options to exclude non-asset games and entitlements from startup

* Add options in settings to not request non-asset games and entitlements.

This greatly improves startup time but reduces functionality a bit. Both
options are disabled by default.

* Remove the old options to exclude Win32 and Mac games as they were no longer relevant.

* Add a performance context manager to log execution time when used.

* Fixed an issue with UbisoftGroup where multiple threads would spawn.
This commit is contained in:
loathingKernel 2023-09-10 19:08:47 +03:00
parent 9d02fec6f8
commit 5319da4bdb
No known key found for this signature in database
GPG key ID: CE0C72D0B53821FD
5 changed files with 125 additions and 83 deletions

View file

@ -1,4 +1,3 @@
import platform
import re
from logging import getLogger
from typing import Tuple
@ -82,20 +81,27 @@ 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.exclude_non_asset_check.setChecked(
self.settings.value("exclude_non_asset", False, bool)
)
self.exclude_non_asset_check.stateChanged.connect(
lambda: self.settings.setValue("exclude_non_asset", self.exclude_non_asset_check.isChecked())
)
self.exclude_entitlements_check.setChecked(
self.settings.value("exclude_entitlements", False, bool)
)
self.exclude_entitlements_check.stateChanged.connect(
lambda: self.settings.setValue("exclude_entitlements", self.exclude_entitlements_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.refresh_game_meta_btn.clicked.connect(self.refresh_game_meta)
self.refresh_game_meta_button.clicked.connect(self.refresh_game_meta)
def refresh_game_meta(self):
self.refresh_game_meta_btn.setDisabled(True)
self.refresh_game_meta_btn.setText(self.tr("Loading"))
self.refresh_game_meta_button.setDisabled(True)
self.refresh_game_meta_button.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")))
worker.signals.finished.connect(lambda: self.refresh_game_meta_button.setDisabled(False))
worker.signals.finished.connect(lambda: self.refresh_game_meta_button.setText(self.tr("Refresh game meta")))
QThreadPool.globalInstance().start(worker)
@staticmethod

View file

@ -9,12 +9,13 @@ from typing import Dict, Iterator, Callable, Optional, List, Union, Iterable, Tu
from PyQt5.QtCore import QObject, pyqtSignal, QSettings, pyqtSlot, QThreadPool, QRunnable, QTimer
from legendary.lfs.eos import EOSOverlayApp
from legendary.models.game import Game, SaveGameFile
from requests import HTTPError
from requests.exceptions import HTTPError, ConnectionError
from rare.lgndr.core import LegendaryCore
from rare.models.base_game import RareSaveGame
from rare.models.game import RareGame, RareEosOverlay
from rare.models.signals import GlobalSignals
from rare.utils.metrics import timelogger
from .image_manager import ImageManager
from .workers import (
QueueWorker,
@ -49,7 +50,7 @@ class RareCore(QObject):
self.__core: Optional[LegendaryCore] = None
self.__image_manager: Optional[ImageManager] = None
self.__start_time = time.time()
self.__start_time = time.perf_counter()
self.args(args)
self.signals(init=True)
@ -273,12 +274,12 @@ class RareCore(QObject):
self.progress.emit(15, self.tr("Preparing library"))
self.__add_games_and_dlcs(*result)
self.progress.emit(100, self.tr("Launching Rare"))
logger.debug(f"Fetch time {time.time() - self.__start_time} seconds")
logger.debug(f"Fetch time {time.perf_counter() - self.__start_time} seconds")
QTimer.singleShot(100, self.__post_init)
self.completed.emit()
def fetch(self):
self.__start_time = time.time()
self.__start_time = time.perf_counter()
fetch_worker = FetchWorker(self.__core, self.__args)
fetch_worker.signals.progress.connect(self.progress)
fetch_worker.signals.result.connect(self.__on_fetch_result)
@ -286,10 +287,10 @@ class RareCore(QObject):
def fetch_saves(self):
def __fetch() -> None:
start_time = time.time()
saves_dict: Dict[str, List[SaveGameFile]] = {}
try:
saves_list = self.__core.get_save_games()
with timelogger(logger, "Request saves"):
saves_list = self.__core.get_save_games()
for s in saves_list:
if s.app_name not in saves_dict.keys():
saves_dict[s.app_name] = [s]
@ -300,26 +301,28 @@ class RareCore(QObject):
continue
self.__library[app_name].load_saves(saves)
except (HTTPError, ConnectionError) as e:
logger.error(f"Exception while fetching saves from EGS: {e}")
logger.error(f"Exception while fetching saves from EGS.")
logger.error(e)
return
logger.debug(f"Saves: {len(saves_dict)}")
logger.debug(f"Request saves: {time.time() - start_time} seconds")
logger.info(f"Saves: {len(saves_dict)}")
saves_worker = QRunnable.create(__fetch)
QThreadPool.globalInstance().start(saves_worker)
def fetch_entitlements(self) -> None:
def __fetch() -> None:
start_time = time.time()
try:
entitlements = self.__core.egs.get_user_entitlements()
if (entitlements := self.__core.lgd.entitlements) is None:
with timelogger(logger, "Request entitlements"):
entitlements = self.__core.egs.get_user_entitlements()
self.__core.lgd.entitlements = entitlements
for game in self.__library.values():
game.grant_date()
except (HTTPError, ConnectionError) as e:
logger.error(f"Failed to retrieve user entitlements from EGS: {e}")
logger.error(f"Exception while fetching entitlements from EGS.")
logger.error(e)
return
logger.debug(f"Entitlements: {len(list(entitlements))}")
logger.debug(f"Request Entitlements: {time.time() - start_time} seconds")
logger.info(f"Entitlements: {len(list(entitlements))}")
entitlements_worker = QRunnable.create(__fetch)
QThreadPool.globalInstance().start(entitlements_worker)
@ -331,7 +334,7 @@ class RareCore(QObject):
def __post_init(self) -> None:
if not self.__args.offline:
self.fetch_saves()
self.fetch_entitlements()
# self.fetch_entitlements()
self.resolve_origin()
@property

View file

@ -1,12 +1,12 @@
import time
from argparse import Namespace
from enum import IntEnum
from logging import getLogger
from PyQt5.QtCore import QObject, pyqtSignal
from requests.exceptions import ConnectionError, HTTPError
from PyQt5.QtCore import QObject, pyqtSignal, QSettings
from requests.exceptions import HTTPError, ConnectionError
from rare.lgndr.core import LegendaryCore
from rare.utils.metrics import timelogger
from .worker import Worker
logger = getLogger("FetchWorker")
@ -27,44 +27,61 @@ class FetchWorker(Worker):
self.signals = FetchWorker.Signals()
self.core = core
self.args = args
self.exclude_non_asset = QSettings().value("exclude_non_asset", False, bool)
self.exclude_entitlements = QSettings().value("exclude_entitlements", False, bool)
def run_real(self):
progress = 0
# Fetch regular EGL games with assets
self.signals.progress.emit(0, self.signals.tr("Updating game metadata"))
start_time = time.time()
games, dlc_dict = self.core.get_game_and_dlc_list(
update_assets=not self.args.offline, platform="Windows", skip_ue=False
)
logger.debug(f"Games {len(games)}, games with DLCs {len(dlc_dict)}")
logger.debug(f"Request games: {time.time() - start_time} seconds")
self.signals.progress.emit(progress, self.signals.tr("Updating game metadata"))
with timelogger(logger, "Request games"):
games, dlc_dict = self.core.get_game_and_dlc_list(
update_assets=not self.args.offline, platform="Windows", skip_ue=False
)
progress += 10
logger.info(f"Games: {len(games)}. Games with DLCs {len(dlc_dict)}")
# Fetch non-asset games
self.signals.progress.emit(10, self.signals.tr("Updating non-asset metadata"))
start_time = time.time()
try:
na_games, na_dlc_dict = self.core.get_non_asset_library_items(force_refresh=False, skip_ue=False)
except (HTTPError, ConnectionError) as e:
logger.warning(f"Exception while fetching non asset games from EGS: {e}")
na_games, na_dlc_dict = ([], {})
# FIXME:
# This is here because of broken appIds from Epic:
# https://discord.com/channels/826881530310819914/884510635642216499/1111321692703305729
# There is a tab character in the appId of Fallout New Vegas: Honest Hearts DLC, this breaks metadata storage
# on Windows as they can't handle tabs at the end of the filename (?)
# Legendary and Heroic are also affected, but it completely breaks Rare, so dodge it for now pending a fix.
except Exception as e:
logger.error(f"Exception while fetching non asset games from EGS: {e}")
na_games, na_dlc_dict = ([], {})
logger.debug(f"Non-asset {len(na_games)}, games with non-asset DLCs {len(na_dlc_dict)}")
logger.debug(f"Request non-asset: {time.time() - start_time} seconds")
if not self.exclude_non_asset:
self.signals.progress.emit(progress, self.signals.tr("Updating non-asset metadata"))
try:
with timelogger(logger, "Request non-asset"):
na_games, na_dlc_dict = self.core.get_non_asset_library_items(force_refresh=False, skip_ue=False)
except (HTTPError, ConnectionError) as e:
logger.error(f"Exception while fetching non asset games from EGS.")
logger.error(e)
na_games, na_dlc_dict = ([], {})
# FIXME:
# This is here because of broken appIds from Epic:
# https://discord.com/channels/826881530310819914/884510635642216499/1111321692703305729
# There is a tab character in the appId of Fallout New Vegas: Honest Hearts DLC, this breaks metadata storage
# on Windows as they can't handle tabs at the end of the filename (?)
# Legendary and Heroic are also affected, but it completely breaks Rare, so dodge it for now pending a fix.
except Exception as e:
logger.error(f"Exception while fetching non asset games from EGS.")
logger.error(e)
na_games, na_dlc_dict = ([], {})
progress += 10
logger.info(f"Non-asset: {len(na_games)}. Non-asset with DLCs: {len(na_dlc_dict)}")
# Combine the two games lists and the two dlc dictionaries between regular and non-asset results
games += na_games
for catalog_id, dlcs in na_dlc_dict.items():
if catalog_id in dlc_dict.keys():
dlc_dict[catalog_id] += dlcs
else:
dlc_dict[catalog_id] = dlcs
logger.debug(f"Games {len(games)}, games with DLCs {len(dlc_dict)}")
# Combine the two games lists and the two dlc dictionaries between regular and non-asset results
games += na_games
for catalog_id, dlcs in na_dlc_dict.items():
if catalog_id in dlc_dict.keys():
dlc_dict[catalog_id] += dlcs
else:
dlc_dict[catalog_id] = dlcs
logger.info(f"Games: {len(games)}. Games with DLCs: {len(dlc_dict)}")
if not self.exclude_entitlements:
# Get entitlements, Ubisoft integration also uses them
self.signals.progress.emit(progress, self.signals.tr("Updating entitlements"))
with timelogger(logger, "Request entitlements"):
entitlements = self.core.egs.get_user_entitlements()
self.core.lgd.entitlements = entitlements
progress += 10
logger.info(f"Entitlements: {len(list(entitlements))}")
self.signals.progress.emit(progress, self.signals.tr("Preparing games"))
self.signals.result.emit((games, dlc_dict), FetchWorker.Result.COMBINED)

View file

@ -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.9
#
# 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.
@ -128,17 +128,17 @@ class Ui_LegendarySettings(object):
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.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.meta_layout = QtWidgets.QVBoxLayout(self.meta_group)
self.meta_layout.setObjectName("meta_layout")
self.exclude_non_asset_check = QtWidgets.QCheckBox(self.meta_group)
self.exclude_non_asset_check.setObjectName("exclude_non_asset_check")
self.meta_layout.addWidget(self.exclude_non_asset_check)
self.exclude_entitlements_check = QtWidgets.QCheckBox(self.meta_group)
self.exclude_entitlements_check.setObjectName("exclude_entitlements_check")
self.meta_layout.addWidget(self.exclude_entitlements_check)
self.refresh_game_meta_button = QtWidgets.QPushButton(self.meta_group)
self.refresh_game_meta_button.setObjectName("refresh_game_meta_button")
self.meta_layout.addWidget(self.refresh_game_meta_button)
self.right_layout.addWidget(self.meta_group)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.right_layout.addItem(spacerItem1)
@ -163,9 +163,15 @@ class Ui_LegendarySettings(object):
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.exclude_non_asset_check.setToolTip(_translate("LegendarySettings", "Do not load metadata for non-asset games (i.e. Origin games) on start-up.\n"
"\n"
"Disabling this greatly improves start-up time, but some games might not be visible in your library."))
self.exclude_non_asset_check.setText(_translate("LegendarySettings", "Exclude non-asset games"))
self.exclude_entitlements_check.setToolTip(_translate("LegendarySettings", "Do not load entitlement data (i.e game\'s date of purchase) on start-up.\n"
"\n"
"Disabling this greatly improves start-up time, but some library filters may not work."))
self.exclude_entitlements_check.setText(_translate("LegendarySettings", "Exclude entitlements"))
self.refresh_game_meta_button.setText(_translate("LegendarySettings", "Refresh game meta"))
if __name__ == "__main__":

View file

@ -231,23 +231,33 @@
<property name="title">
<string>Game metadata</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QVBoxLayout" name="meta_layout">
<item>
<widget class="QCheckBox" name="win32_cb">
<widget class="QCheckBox" name="exclude_non_asset_check">
<property name="toolTip">
<string>Do not load metadata for non-asset games (i.e. Origin games) on start-up.
Disabling this greatly improves start-up time, but some games might not be visible in your library.</string>
</property>
<property name="text">
<string>Load 32bit data</string>
<string>Exclude non-asset games</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mac_cb">
<widget class="QCheckBox" name="exclude_entitlements_check">
<property name="toolTip">
<string>Do not load entitlement data (i.e game's date of purchase) on start-up.
Disabling this greatly improves start-up time, but some library filters may not work.</string>
</property>
<property name="text">
<string>Load MacOS data</string>
<string>Exclude entitlements</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="refresh_game_meta_btn">
<widget class="QPushButton" name="refresh_game_meta_button">
<property name="text">
<string>Refresh game meta</string>
</property>