RareCore: Fetch **ALL** saves during launch
To save time and requests, bulk get saves for all games and load them into each respective RareGame. Co-authored-by: Dummerle <44114474+dummerle@users.noreply.github.com>
This commit is contained in:
parent
f6189772d0
commit
81ec9bf772
|
@ -1,4 +1,5 @@
|
|||
from abc import abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from enum import IntEnum
|
||||
from logging import getLogger
|
||||
|
@ -13,6 +14,14 @@ from rare.models.install import UninstallOptionsModel, InstallOptionsModel
|
|||
logger = getLogger("RareGameBase")
|
||||
|
||||
|
||||
@dataclass
|
||||
class RareSaveGame:
|
||||
file: SaveGameFile
|
||||
status: SaveGameStatus = SaveGameStatus.NO_SAVE
|
||||
dt_local: Optional[datetime] = None
|
||||
dt_remote: datetime = None
|
||||
|
||||
|
||||
class RareGameBase(QObject):
|
||||
|
||||
class State(IntEnum):
|
||||
|
@ -122,7 +131,7 @@ class RareGameSlim(RareGameBase):
|
|||
super(RareGameSlim, self).__init__(legendary_core, game)
|
||||
# None if origin or not installed
|
||||
self.igame: Optional[InstalledGame] = self.core.get_installed_game(game.app_name)
|
||||
self.saves: List[SaveGameFile] = []
|
||||
self.saves: List[RareSaveGame] = []
|
||||
|
||||
@property
|
||||
def is_installed(self) -> bool:
|
||||
|
@ -162,16 +171,18 @@ class RareGameSlim(RareGameBase):
|
|||
return None
|
||||
|
||||
@property
|
||||
def latest_save(self) -> Optional[SaveGameFile]:
|
||||
def latest_save(self) -> Optional[RareSaveGame]:
|
||||
if self.saves:
|
||||
self.saves.sort(key=lambda s: s.datetime, reverse=True)
|
||||
return self.saves[0]
|
||||
saves = sorted(self.saves, key=lambda s: s.file.datetime, reverse=True)
|
||||
return saves[0]
|
||||
return None
|
||||
|
||||
@property
|
||||
def save_game_state(self) -> (SaveGameStatus, (datetime, datetime)):
|
||||
if self.saves and self.save_path :
|
||||
return self.core.check_savegame_state(self.save_path, self.latest_save)
|
||||
if self.saves and self.save_path:
|
||||
latest = self.latest_save
|
||||
return latest.status, (latest.dt_local, latest.dt_remote)
|
||||
# return self.core.check_savegame_state(self.save_path, self.latest_save.save)
|
||||
return SaveGameStatus.NO_SAVE, (None, None)
|
||||
|
||||
def upload_saves(self, thread=True):
|
||||
|
@ -203,7 +214,7 @@ class RareGameSlim(RareGameBase):
|
|||
def _download():
|
||||
logger.info(f"Downloading save for {self.title}")
|
||||
self.state = RareGameSlim.State.SYNCING
|
||||
self.core.download_saves(self.app_name, self.latest_save.manifest_name, self.save_path)
|
||||
self.core.download_saves(self.app_name, self.latest_save.file.manifest_name, self.save_path)
|
||||
self.state = RareGameSlim.State.IDLE
|
||||
self.update_saves()
|
||||
|
||||
|
@ -222,8 +233,21 @@ class RareGameSlim(RareGameBase):
|
|||
else:
|
||||
_download()
|
||||
|
||||
def load_saves(self, saves: List[SaveGameFile]):
|
||||
""" Use only in a thread """
|
||||
self.saves.clear()
|
||||
for save in saves:
|
||||
if self.save_path:
|
||||
status, (dt_local, dt_remote) = self.core.check_savegame_state(self.save_path, save)
|
||||
rsave = RareSaveGame(save, status, dt_local, dt_remote)
|
||||
else:
|
||||
rsave = RareSaveGame(save, SaveGameStatus.SAME_AGE, dt_local=None, dt_remote=save.datetime)
|
||||
self.saves.append(rsave)
|
||||
|
||||
def update_saves(self):
|
||||
self.saves = self.core.get_save_games(self.app_name)
|
||||
""" Use only in a thread """
|
||||
saves = self.core.get_save_games(self.app_name)
|
||||
self.load_saves(saves)
|
||||
self.signals.widget.update.emit()
|
||||
|
||||
@property
|
||||
|
@ -231,3 +255,15 @@ class RareGameSlim(RareGameBase):
|
|||
status, (_, _) = self.save_game_state
|
||||
return (status == SaveGameStatus.SAME_AGE) \
|
||||
or (status == SaveGameStatus.NO_SAVE)
|
||||
|
||||
@property
|
||||
def raw_save_path(self) -> str:
|
||||
if self.game.supports_cloud_saves:
|
||||
return self.game.metadata.get("customAttributes", {}).get("CloudSaveFolder", {}).get("value")
|
||||
return ""
|
||||
|
||||
@property
|
||||
def raw_save_path_mac(self) -> str:
|
||||
if self.game.supports_mac_cloud_saves:
|
||||
return self.game.metadata.get("customAttributes", {}).get('CloudSaveFolder_MAC', {}).get('value')
|
||||
return ""
|
||||
|
|
|
@ -401,18 +401,6 @@ class RareGame(RareGameSlim):
|
|||
def folder_name(self) -> str:
|
||||
return self.game.metadata.get("customAttributes", {}).get("FolderName", {}).get("value")
|
||||
|
||||
@property
|
||||
def raw_save_path(self) -> str:
|
||||
if self.game.supports_cloud_saves:
|
||||
return self.game.metadata.get("customAttributes", {}).get("CloudSaveFolder", {}).get("value")
|
||||
return ""
|
||||
|
||||
@property
|
||||
def raw_save_path_mac(self) -> str:
|
||||
if self.game.supports_mac_cloud_saves:
|
||||
return self.game.metadata.get("customAttributes", {}).get('CloudSaveFolder_MAC', {}).get('value')
|
||||
return ""
|
||||
|
||||
@property
|
||||
def save_path(self) -> Optional[str]:
|
||||
return super(RareGame, self).save_path
|
||||
|
|
|
@ -6,7 +6,7 @@ from itertools import chain
|
|||
from logging import getLogger
|
||||
from typing import Dict, Iterator, Callable, Tuple, Optional, List, Union
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, QSettings, pyqtSlot, QThreadPool
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, QSettings, pyqtSlot, QThreadPool, QRunnable
|
||||
from legendary.lfs.eos import EOSOverlayApp
|
||||
from legendary.models.game import Game, SaveGameFile
|
||||
|
||||
|
@ -27,7 +27,7 @@ from .workers import (
|
|||
)
|
||||
from .workers.uninstall import uninstall_game
|
||||
from .workers.worker import QueueWorkerInfo, QueueWorkerState
|
||||
from rare.models.base_game import RareGameBase
|
||||
from rare.models.base_game import RareSaveGame
|
||||
|
||||
logger = getLogger("RareCore")
|
||||
|
||||
|
@ -272,11 +272,14 @@ class RareCore(QObject):
|
|||
if res_type == FetchWorker.Result.GAMES:
|
||||
games, dlc_dict = result
|
||||
self.__add_games_and_dlcs(games, dlc_dict)
|
||||
self.fetch_extra()
|
||||
self.__games_fetched = True
|
||||
status = "Loaded games for Windows"
|
||||
if res_type == FetchWorker.Result.NON_ASSET:
|
||||
games, dlc_dict = result
|
||||
self.__add_games_and_dlcs(games, dlc_dict)
|
||||
self.fetch_saves()
|
||||
self.resolve_origin()
|
||||
self.__non_asset_fetched = True
|
||||
status = "Loaded games without assets"
|
||||
if res_type == FetchWorker.Result.ORIGIN:
|
||||
|
@ -284,8 +287,8 @@ class RareCore(QObject):
|
|||
status = "Resolved Origin installation status"
|
||||
if res_type == FetchWorker.Result.SAVES:
|
||||
saves, _ = result
|
||||
for save in saves:
|
||||
self.__games[save.app_name].saves.append(save)
|
||||
for app_name, saves in saves.items():
|
||||
self.__games[app_name].load_saves(saves)
|
||||
self.__saves_fetched = True
|
||||
status = "Loaded save games"
|
||||
if res_type == FetchWorker.Result.ENTITLEMENTS:
|
||||
|
@ -321,8 +324,7 @@ class RareCore(QObject):
|
|||
self.start_time = time.time()
|
||||
games_worker = GamesWorker(self.__core, self.__args)
|
||||
games_worker.signals.result.connect(self.handle_result)
|
||||
games_worker.signals.finished.connect(self.fetch_saves)
|
||||
games_worker.signals.finished.connect(self.fetch_extra)
|
||||
|
||||
QThreadPool.globalInstance().start(games_worker)
|
||||
|
||||
def fetch_saves(self):
|
||||
|
@ -341,12 +343,14 @@ class RareCore(QObject):
|
|||
def fetch_extra(self):
|
||||
non_asset_worker = NonAssetWorker(self.__core, self.__args)
|
||||
non_asset_worker.signals.result.connect(self.handle_result)
|
||||
non_asset_worker.signals.finished.connect(self.resolve_origin)
|
||||
QThreadPool.globalInstance().start(non_asset_worker)
|
||||
|
||||
entitlements_worker = EntitlementsWorker(self.__core, self.__args)
|
||||
entitlements_worker.signals.result.connect(self.handle_result)
|
||||
QThreadPool.globalInstance().start(entitlements_worker)
|
||||
if not self.__args.offline:
|
||||
entitlements_worker = EntitlementsWorker(self.__core, self.__args)
|
||||
entitlements_worker.signals.result.connect(self.handle_result)
|
||||
QThreadPool.globalInstance().start(entitlements_worker)
|
||||
else:
|
||||
self.__entitlements_fetched = True
|
||||
|
||||
def load_pixmaps(self) -> None:
|
||||
"""
|
||||
|
@ -428,7 +432,7 @@ class RareCore(QObject):
|
|||
return self.__filter_games(lambda game: game.has_update)
|
||||
|
||||
@property
|
||||
def saves(self) -> Iterator[SaveGameFile]:
|
||||
def saves(self) -> Iterator[RareSaveGame]:
|
||||
"""!
|
||||
SaveGameFiles across games
|
||||
"""
|
||||
|
|
|
@ -2,8 +2,10 @@ import time
|
|||
from argparse import Namespace
|
||||
from enum import IntEnum
|
||||
from logging import getLogger
|
||||
from typing import Dict, List
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal
|
||||
from legendary.models.game import SaveGameFile
|
||||
from requests.exceptions import ConnectionError, HTTPError
|
||||
|
||||
from rare.lgndr.core import LegendaryCore
|
||||
|
@ -22,7 +24,6 @@ class FetchWorker(Worker):
|
|||
|
||||
class Signals(QObject):
|
||||
result = pyqtSignal(object, int)
|
||||
finished = pyqtSignal()
|
||||
|
||||
def __init__(self, core: LegendaryCore, args: Namespace):
|
||||
super(Worker, self).__init__()
|
||||
|
@ -38,7 +39,6 @@ class GamesWorker(FetchWorker):
|
|||
self.signals.result.emit(result, FetchWorker.Result.GAMES)
|
||||
logger.debug(f"Games: {len(result[0])}, DLCs {len(result[1])}")
|
||||
logger.debug(f"Request Games: {time.time() - start_time} seconds")
|
||||
self.signals.finished.emit()
|
||||
|
||||
|
||||
class NonAssetWorker(FetchWorker):
|
||||
|
@ -52,7 +52,6 @@ class NonAssetWorker(FetchWorker):
|
|||
self.signals.result.emit(result, FetchWorker.Result.NON_ASSET)
|
||||
logger.debug(f"Non asset: {len(result[0])}, DLCs {len(result[1])}")
|
||||
logger.debug(f"Request Non Asset: {time.time() - start_time} seconds")
|
||||
self.signals.finished.emit()
|
||||
|
||||
|
||||
class EntitlementsWorker(FetchWorker):
|
||||
|
@ -66,18 +65,22 @@ class EntitlementsWorker(FetchWorker):
|
|||
self.signals.result.emit(entitlements, FetchWorker.Result.ENTITLEMENTS)
|
||||
logger.debug(f"Entitlements: {len(list(entitlements))}")
|
||||
logger.debug(f"Request Entitlements: {time.time() - start_time} seconds")
|
||||
self.signals.finished.emit()
|
||||
|
||||
|
||||
class SavesWorker(FetchWorker):
|
||||
def run_real(self):
|
||||
start_time = time.time()
|
||||
result: Dict[str, List[SaveGameFile]] = {}
|
||||
try:
|
||||
result = self.core.get_save_games()
|
||||
saves = self.core.get_save_games()
|
||||
for save in saves:
|
||||
if not save.app_name in result.keys():
|
||||
result[save.app_name] = [save]
|
||||
else:
|
||||
result[save.app_name].append(save)
|
||||
except (HTTPError, ConnectionError) as e:
|
||||
logger.warning(f"Exception while fetching saves fromt EGS: {e}")
|
||||
result = list()
|
||||
logger.warning(f"Exception while fetching saves from EGS: {e}")
|
||||
result = {}
|
||||
self.signals.result.emit((result, {}), FetchWorker.Result.SAVES)
|
||||
logger.debug(f"Saves: {len(result)}")
|
||||
logger.debug(f"Request saves: {time.time() - start_time} seconds")
|
||||
self.signals.finished.emit()
|
||||
|
|
|
@ -160,7 +160,7 @@ def path_size(path: Union[str, os.PathLike]) -> int:
|
|||
return sum(
|
||||
os.stat(os.path.join(dp, f)).st_size
|
||||
for dp, dn, filenames in os.walk(path)
|
||||
for f in filenames if os.path.isfile(f)
|
||||
for f in filenames if os.path.isfile(os.path.join(dp,f ))
|
||||
)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue