1
0
Fork 0
mirror of synced 2024-05-18 19:42:54 +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 a246f4ee16
commit 3c43da1594
5 changed files with 127 additions and 59 deletions

View file

@ -116,6 +116,19 @@ class LegendarySettings(QWidget, Ui_LegendarySettings):
lambda: self.settings.setValue("unreal_meta", self.fetch_unreal_check.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.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)

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,
@ -50,7 +51,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)
@ -310,12 +311,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)
@ -323,10 +324,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]
@ -337,26 +338,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)
@ -368,7 +371,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,13 +1,13 @@
import platform
import time
from argparse import Namespace
from enum import IntEnum
from logging import getLogger
from PyQt5.QtCore import QObject, pyqtSignal, QSettings
from requests.exceptions import ConnectionError, HTTPError
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")
@ -32,13 +32,11 @@ class FetchWorker(Worker):
def run_real(self):
# Fetch regular EGL games with assets
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])
need_windows = not any([want_win32, want_macos, need_macos, self.args.debug]) and not self.args.offline
if want_win32 or self.args.debug:
logger.info(
@ -47,9 +45,10 @@ class FetchWorker(Worker):
"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
)
with timelogger(logger, "Request Win32 games"):
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(
@ -58,9 +57,10 @@ class FetchWorker(Worker):
"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
)
with timelogger(logger, "Request macOS games"):
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"))
@ -68,39 +68,53 @@ class FetchWorker(Worker):
"Requesting Windows metadata, %s Unreal engine",
"with" if want_unreal else "without"
)
games, dlc_dict = self.core.get_game_and_dlc_list(
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")
with timelogger(logger, "Request Windows games"):
games, dlc_dict = self.core.get_game_and_dlc_list(
update_assets=need_windows, platform="Windows", skip_ue=not want_unreal
)
logger.info(f"Games: %s. Games with DLCs: %s", len(games), len(dlc_dict))
want_non_asset = not self.settings.value("exclude_non_asset", False, bool)
want_entitlements = not self.settings.value("exclude_entitlements", False, bool)
# Fetch non-asset games
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)
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 want_non_asset:
self.signals.progress.emit(30, self.signals.tr("Updating non-asset game 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("Network 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("General exception while fetching non asset games from EGS.")
logger.error(e)
na_games, na_dlc_dict = ([], {})
logger.info("Non-asset: %s. Non-asset with DLCs: %s", len(na_games), 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 want_entitlements:
# Get entitlements, Ubisoft integration also uses them
self.signals.progress.emit(40, self.signals.tr("Updating entitlements"))
with timelogger(logger, "Request entitlements"):
entitlements = self.core.egs.get_user_entitlements()
self.core.lgd.entitlements = entitlements
logger.info(f"Entitlements: {len(list(entitlements))}")
self.signals.progress.emit(50, 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.10
# Created by: PyQt5 UI code generator 5.15.7
#
# 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.
@ -139,6 +139,12 @@ class Ui_LegendarySettings(object):
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.exclude_non_asset_check = QtWidgets.QCheckBox(self.meta_group)
self.exclude_non_asset_check.setObjectName("exclude_non_asset_check")
self.verticalLayout_2.addWidget(self.exclude_non_asset_check)
self.exclude_entitlements_check = QtWidgets.QCheckBox(self.meta_group)
self.exclude_entitlements_check.setObjectName("exclude_entitlements_check")
self.verticalLayout_2.addWidget(self.exclude_entitlements_check)
self.metadata_info = QtWidgets.QLabel(self.metadata_group)
font = QtGui.QFont()
font.setItalic(True)
@ -175,6 +181,14 @@ class Ui_LegendarySettings(object):
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.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.metadata_info.setText(_translate("LegendarySettings", "Restart Rare to apply"))
self.refresh_metadata_button.setText(_translate("LegendarySettings", "Refresh metadata"))

View file

@ -231,7 +231,7 @@
<property name="title">
<string>Platforms</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QVBoxLayout" name="metadata_layout">
<item>
<widget class="QCheckBox" name="fetch_win32_check">
<property name="text">
@ -253,6 +253,30 @@
</property>
</widget>
</item>
<item>
<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>Exclude non-asset games</string>
</property>
</widget>
</item>
<item>
<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>Exclude entitlements</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="metadata_info">
<property name="font">