CloudSaveUtils: Transition to RareGame
Signed-off-by: loathingKernel <142770+loathingKernel@users.noreply.github.com>
This commit is contained in:
parent
79bae4374d
commit
c7a70a9a75
|
@ -6,9 +6,10 @@ from typing import Union, List, Dict
|
|||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, QRunnable, QThreadPool, Qt, QSettings
|
||||
from PyQt5.QtWidgets import QDialog, QMessageBox, QSizePolicy, QLayout, QApplication
|
||||
|
||||
from legendary.core import LegendaryCore
|
||||
from legendary.models.game import SaveGameStatus, InstalledGame, SaveGameFile
|
||||
|
||||
from rare.models.game import RareGame
|
||||
from rare.shared import LegendaryCoreSingleton, ArgumentsSingleton, ApiResultsSingleton
|
||||
from rare.ui.components.dialogs.sync_save_dialog import Ui_SyncSaveDialog
|
||||
from rare.utils.misc import icon
|
||||
|
@ -18,26 +19,26 @@ logger = getLogger("Cloud Saves")
|
|||
|
||||
@dataclass
|
||||
class UploadModel:
|
||||
app_name: str
|
||||
rgame: RareGame
|
||||
date_time: datetime.datetime
|
||||
path: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class DownloadModel:
|
||||
app_name: str
|
||||
rgame: RareGame
|
||||
latest_save: SaveGameFile
|
||||
path: str
|
||||
|
||||
|
||||
class SaveSignals(QObject):
|
||||
finished = pyqtSignal(str, str)
|
||||
|
||||
|
||||
class SaveWorker(QRunnable):
|
||||
|
||||
class Signals(QObject):
|
||||
finished = pyqtSignal(RareGame, str)
|
||||
|
||||
def __init__(self, model: Union[UploadModel, DownloadModel]):
|
||||
super(SaveWorker, self).__init__()
|
||||
self.signals = SaveSignals()
|
||||
self.signals = SaveWorker.Signals()
|
||||
self.setAutoDelete(True)
|
||||
self.core = LegendaryCoreSingleton()
|
||||
self.api_results = ApiResultsSingleton()
|
||||
|
@ -47,29 +48,29 @@ class SaveWorker(QRunnable):
|
|||
try:
|
||||
if isinstance(self.model, DownloadModel):
|
||||
self.core.download_saves(
|
||||
self.model.app_name,
|
||||
self.model.rgame.app_name,
|
||||
self.model.latest_save.manifest_name,
|
||||
self.model.path,
|
||||
)
|
||||
else:
|
||||
self.core.upload_save(
|
||||
self.model.app_name, self.model.path, self.model.date_time
|
||||
self.model.rgame.app_name, self.model.path, self.model.date_time
|
||||
)
|
||||
except Exception as e:
|
||||
self.signals.finished.emit(str(e), self.model.app_name)
|
||||
self.signals.finished.emit(self.model.rgame, str(e))
|
||||
logger.error(str(e))
|
||||
return
|
||||
try:
|
||||
if isinstance(self.model, UploadModel):
|
||||
logger.info("Updating cloud saves...")
|
||||
result = self.core.get_save_games(self.model.app_name)
|
||||
result = self.core.get_save_games(self.model.rgame.app_name)
|
||||
self.api_results.saves = result
|
||||
except Exception as e:
|
||||
self.signals.finished.emit(str(e), self.model.app_name)
|
||||
self.signals.finished.emit(self.model.rgame, str(e))
|
||||
logger.error(str(e))
|
||||
return
|
||||
|
||||
self.signals.finished.emit("", self.model.app_name)
|
||||
self.signals.finished.emit(self.model.rgame, "")
|
||||
|
||||
|
||||
class CloudSaveDialog(QDialog, Ui_SyncSaveDialog):
|
||||
|
@ -123,7 +124,7 @@ class CloudSaveDialog(QDialog, Ui_SyncSaveDialog):
|
|||
|
||||
|
||||
class CloudSaveUtils(QObject):
|
||||
sync_finished = pyqtSignal(str)
|
||||
sync_finished = pyqtSignal(RareGame)
|
||||
|
||||
def __init__(self):
|
||||
super(CloudSaveUtils, self).__init__()
|
||||
|
@ -152,24 +153,23 @@ class CloudSaveUtils(QObject):
|
|||
latest_saves[s.app_name] = s
|
||||
return latest_saves
|
||||
|
||||
def sync_before_launch_game(self, app_name, ignore_settings=False) -> int:
|
||||
def sync_before_launch_game(self, rgame: RareGame, ignore_settings=False) -> int:
|
||||
if not ignore_settings:
|
||||
default = self.settings.value("auto_sync_cloud", True, bool)
|
||||
if not self.settings.value(f"{app_name}/auto_sync_cloud", default, bool):
|
||||
if not self.settings.value(f"{rgame.app_name}/auto_sync_cloud", default, bool):
|
||||
return False
|
||||
|
||||
igame = self.core.get_installed_game(app_name)
|
||||
|
||||
if not igame.save_path:
|
||||
if not rgame.igame.save_path:
|
||||
try:
|
||||
savepath = self.core.get_save_path(app_name)
|
||||
savepath = self.core.get_save_path(rgame.app_name)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
savepath = ""
|
||||
if savepath:
|
||||
igame.save_path = savepath
|
||||
self.core.lgd.set_installed_game(app_name, igame)
|
||||
logger.info(f"Set save path of {igame.title} to {savepath}")
|
||||
rgame.igame.save_path = savepath
|
||||
self.core.lgd.set_installed_game(rgame.app_name, rgame.igame)
|
||||
logger.info(f"Set save path of {rgame.title} to {savepath}")
|
||||
elif not ignore_settings: # sync on startup
|
||||
if (
|
||||
QMessageBox.question(
|
||||
|
@ -177,7 +177,7 @@ class CloudSaveUtils(QObject):
|
|||
"Warning",
|
||||
self.tr(
|
||||
"Could not compute cloud save path. Please set it in Game settings manually. \nDo you want to launch {} anyway?"
|
||||
).format(igame.title),
|
||||
).format(rgame.title),
|
||||
QMessageBox.Yes | QMessageBox.No,
|
||||
QMessageBox.No,
|
||||
)
|
||||
|
@ -197,7 +197,7 @@ class CloudSaveUtils(QObject):
|
|||
return False
|
||||
|
||||
res, (dt_local, dt_remote) = self.core.check_savegame_state(
|
||||
igame.save_path, self.latest_saves.get(app_name)
|
||||
rgame.igame.save_path, self.latest_saves.get(rgame.app_name)
|
||||
)
|
||||
|
||||
if res == SaveGameStatus.NO_SAVE:
|
||||
|
@ -211,43 +211,41 @@ class CloudSaveUtils(QObject):
|
|||
newer = "local"
|
||||
|
||||
if res == SaveGameStatus.REMOTE_NEWER and not dt_local:
|
||||
self.download_saves(igame)
|
||||
self.download_saves(rgame)
|
||||
return True
|
||||
|
||||
elif res == SaveGameStatus.LOCAL_NEWER and not dt_remote:
|
||||
self.upload_saves(igame, dt_local)
|
||||
self.upload_saves(rgame, dt_local)
|
||||
return True
|
||||
|
||||
result = CloudSaveDialog(igame, dt_local, dt_remote, newer).get_action()
|
||||
result = CloudSaveDialog(rgame.igame, dt_local, dt_remote, newer).get_action()
|
||||
if result == CloudSaveDialog.UPLOAD:
|
||||
self.upload_saves(igame, dt_local)
|
||||
self.upload_saves(rgame, dt_local)
|
||||
elif result == CloudSaveDialog.DOWNLOAD:
|
||||
self.download_saves(igame)
|
||||
self.download_saves(rgame)
|
||||
elif result == CloudSaveDialog.CANCEL:
|
||||
raise AssertionError()
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def game_finished(self, app_name, ignore_settings=False, always_ask: bool = False):
|
||||
def game_finished(self, rgame: RareGame, ignore_settings=False, always_ask: bool = False):
|
||||
if not ignore_settings:
|
||||
default = self.settings.value("auto_sync_cloud", True, bool)
|
||||
if not self.settings.value(f"{app_name}/auto_sync_cloud", default, bool):
|
||||
self.sync_finished.emit(app_name)
|
||||
if not self.settings.value(f"{rgame.app_name}/auto_sync_cloud", default, bool):
|
||||
self.sync_finished.emit(rgame)
|
||||
return
|
||||
|
||||
igame = self.core.get_installed_game(app_name)
|
||||
|
||||
if not igame.save_path:
|
||||
if not rgame.igame.save_path:
|
||||
try:
|
||||
savepath = self.core.get_save_path(app_name)
|
||||
savepath = self.core.get_save_path(rgame.app_name)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
savepath = ""
|
||||
if savepath:
|
||||
igame.save_path = savepath
|
||||
self.core.lgd.set_installed_game(app_name, igame)
|
||||
logger.info(f"Set save path of {igame.title} to {savepath}")
|
||||
rgame.igame.save_path = savepath
|
||||
self.core.lgd.set_installed_game(rgame.app_name, rgame.igame)
|
||||
logger.info(f"Set save path of {rgame.title} to {savepath}")
|
||||
else:
|
||||
QMessageBox.warning(
|
||||
None, "Warning", self.tr("No savepath set. Skip syncing with cloud")
|
||||
|
@ -255,11 +253,11 @@ class CloudSaveUtils(QObject):
|
|||
return False
|
||||
|
||||
res, (dt_local, dt_remote) = self.core.check_savegame_state(
|
||||
igame.save_path, self.latest_saves.get(app_name)
|
||||
rgame.igame.save_path, self.latest_saves.get(rgame.app_name)
|
||||
)
|
||||
|
||||
if res == SaveGameStatus.LOCAL_NEWER and not always_ask:
|
||||
self.upload_saves(igame, dt_local)
|
||||
self.upload_saves(rgame, dt_local)
|
||||
return
|
||||
|
||||
elif res == SaveGameStatus.NO_SAVE and not always_ask:
|
||||
|
@ -268,13 +266,13 @@ class CloudSaveUtils(QObject):
|
|||
"No saves",
|
||||
self.tr(
|
||||
"There are no saves local and online. Maybe you have to change save path of {}"
|
||||
).format(igame.title),
|
||||
).format(rgame.title),
|
||||
)
|
||||
self.sync_finished.emit(app_name)
|
||||
self.sync_finished.emit(rgame)
|
||||
return
|
||||
|
||||
elif res == SaveGameStatus.SAME_AGE and not always_ask:
|
||||
self.sync_finished.emit(app_name)
|
||||
self.sync_finished.emit(rgame)
|
||||
return
|
||||
|
||||
# Remote newer
|
||||
|
@ -283,32 +281,32 @@ class CloudSaveUtils(QObject):
|
|||
newer = "remote"
|
||||
elif res == SaveGameStatus.LOCAL_NEWER:
|
||||
newer = "local"
|
||||
result = CloudSaveDialog(igame, dt_local, dt_remote, newer).get_action()
|
||||
result = CloudSaveDialog(rgame.igame, dt_local, dt_remote, newer).get_action()
|
||||
if result == CloudSaveDialog.UPLOAD:
|
||||
self.upload_saves(igame, dt_local)
|
||||
self.upload_saves(rgame, dt_local)
|
||||
elif result == CloudSaveDialog.DOWNLOAD:
|
||||
self.download_saves(igame)
|
||||
self.download_saves(rgame)
|
||||
|
||||
def upload_saves(self, igame: InstalledGame, dt_local):
|
||||
logger.info(f"Uploading saves for {igame.title}")
|
||||
w = SaveWorker(UploadModel(igame.app_name, dt_local, igame.save_path))
|
||||
def upload_saves(self, rgame: RareGame, dt_local):
|
||||
logger.info(f"Uploading saves for {rgame.title}")
|
||||
w = SaveWorker(UploadModel(rgame, dt_local, rgame.igame.save_path))
|
||||
w.signals.finished.connect(self.worker_finished)
|
||||
self.thread_pool.start(w)
|
||||
|
||||
def download_saves(self, igame):
|
||||
logger.info(f"Downloading saves for {igame.title}")
|
||||
def download_saves(self, rgame: RareGame):
|
||||
logger.info(f"Downloading saves for {rgame.title}")
|
||||
w = SaveWorker(
|
||||
DownloadModel(
|
||||
igame.app_name, self.latest_saves.get(igame.app_name), igame.save_path
|
||||
rgame, self.latest_saves.get(rgame.app_name), rgame.igame.save_path
|
||||
)
|
||||
)
|
||||
w.signals.finished.connect(self.worker_finished)
|
||||
self.thread_pool.start(w)
|
||||
|
||||
def worker_finished(self, error_message: str, app_name: str):
|
||||
def worker_finished(self, rgame: RareGame, error_message: str,):
|
||||
if not error_message:
|
||||
|
||||
self.sync_finished.emit(app_name)
|
||||
self.sync_finished.emit(rgame)
|
||||
self.latest_saves = self.get_latest_saves(self.api_results.saves)
|
||||
else:
|
||||
QMessageBox.warning(
|
||||
|
@ -316,7 +314,7 @@ class CloudSaveUtils(QObject):
|
|||
"Warning",
|
||||
self.tr("Syncing with cloud failed: \n {}").format(error_message)
|
||||
)
|
||||
self.sync_finished.emit(app_name)
|
||||
self.sync_finished.emit(rgame)
|
||||
|
||||
|
||||
def test_dialog():
|
||||
|
|
|
@ -4,7 +4,7 @@ import os
|
|||
import platform
|
||||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import QObject, QProcess, pyqtSignal, QUrl, QTimer
|
||||
from PyQt5.QtCore import QObject, QProcess, pyqtSignal, QUrl, QTimer, pyqtSlot
|
||||
from PyQt5.QtCore import QStandardPaths
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
from PyQt5.QtNetwork import QLocalSocket
|
||||
|
@ -26,9 +26,9 @@ logger = getLogger("GameUtils")
|
|||
|
||||
class GameProcess(QObject):
|
||||
# str: app_name, int: exit_code
|
||||
game_finished = pyqtSignal(str, int)
|
||||
game_finished = pyqtSignal(RareGame, int)
|
||||
# str: app_name
|
||||
game_launched = pyqtSignal(str)
|
||||
game_launched = pyqtSignal(RareGame)
|
||||
|
||||
def __init__(self, rgame: RareGame, on_startup=False, always_ask_sync: bool = False):
|
||||
super(GameProcess, self).__init__()
|
||||
|
@ -68,7 +68,7 @@ class GameProcess(QObject):
|
|||
if self.tried_connections > 50: # 10 seconds
|
||||
QMessageBox.warning(None, "Error", self.tr("Connection to game process failed (Timeout)"))
|
||||
self.timer.stop()
|
||||
self.game_finished.emit(self.rgame.app_name, 1)
|
||||
self.game_finished.emit(self.rgame, 1)
|
||||
|
||||
def _message_available(self):
|
||||
message = self.socket.readAll().data()
|
||||
|
@ -103,7 +103,7 @@ class GameProcess(QObject):
|
|||
model = message_models.StateChangedModel.from_json(data)
|
||||
if model.new_state == message_models.StateChangedModel.States.started:
|
||||
logger.info("Launched Game")
|
||||
self.game_launched.emit(self.rgame.app_name)
|
||||
self.game_launched.emit(self.rgame)
|
||||
meta_data = self.rgame.metadata
|
||||
meta_data.last_played = datetime.datetime.now()
|
||||
self.rgame.save_metadata()
|
||||
|
@ -116,7 +116,7 @@ class GameProcess(QObject):
|
|||
logger.info(f"Found {self.rgame.app_name} ({self.rgame.app_title}) running at startup")
|
||||
|
||||
# FIXME run this after startup, widgets do not exist at this time
|
||||
QTimer.singleShot(1000, lambda: self.game_launched.emit(self.rgame.app_name))
|
||||
QTimer.singleShot(1000, lambda: self.game_launched.emit(self.rgame))
|
||||
|
||||
def _error_occurred(self, _):
|
||||
if self.on_startup:
|
||||
|
@ -126,7 +126,7 @@ class GameProcess(QObject):
|
|||
logger.error(f"{self.rgame.app_name} ({self.rgame.app_title}): {self.socket.errorString()}")
|
||||
|
||||
def _game_finished(self, exit_code: int):
|
||||
self.game_finished.emit(self.rgame.app_name, exit_code)
|
||||
self.game_finished.emit(self.rgame, exit_code)
|
||||
|
||||
|
||||
def uninstall_game(core: LegendaryCore, app_name: str, keep_files=False, keep_config=False):
|
||||
|
@ -198,32 +198,30 @@ class GameUtils(QObject):
|
|||
game_process.game_launched.connect(self.game_launched.emit)
|
||||
self.running_games[rgame.app_name] = game_process
|
||||
|
||||
def uninstall_game(self, app_name) -> bool:
|
||||
def uninstall_game(self, rgame: RareGame) -> bool:
|
||||
# returns if uninstalled
|
||||
game = self.core.get_game(app_name)
|
||||
igame = self.core.get_installed_game(app_name)
|
||||
if not os.path.exists(igame.install_path):
|
||||
if not os.path.exists(rgame.igame.install_path):
|
||||
if QMessageBox.Yes == QMessageBox.question(
|
||||
None,
|
||||
self.tr("Uninstall - {}").format(igame.title),
|
||||
self.tr("Uninstall - {}").format(rgame.igame.title),
|
||||
self.tr(
|
||||
"Game files of {} do not exist. Remove it from installed games?"
|
||||
).format(igame.title),
|
||||
).format(rgame.igame.title),
|
||||
QMessageBox.Yes | QMessageBox.No,
|
||||
QMessageBox.Yes,
|
||||
):
|
||||
self.core.lgd.remove_installed_game(app_name)
|
||||
self.core.lgd.remove_installed_game(rgame.app_name)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
proceed, keep_files, keep_config = UninstallDialog(game).get_options()
|
||||
proceed, keep_files, keep_config = UninstallDialog(rgame.game).get_options()
|
||||
if not proceed:
|
||||
return False
|
||||
success, message = uninstall_game(self.core, game.app_name, keep_files, keep_config)
|
||||
success, message = uninstall_game(self.core, rgame.app_name, keep_files, keep_config)
|
||||
if not success:
|
||||
QMessageBox.warning(None, self.tr("Uninstall - {}").format(igame.title), message, QMessageBox.Close)
|
||||
self.signals.game.uninstalled.emit(app_name)
|
||||
QMessageBox.warning(None, self.tr("Uninstall - {}").format(rgame.title), message, QMessageBox.Close)
|
||||
self.signals.game.uninstalled.emit(rgame.app_name)
|
||||
return True
|
||||
|
||||
def prepare_launch(
|
||||
|
@ -234,18 +232,18 @@ class GameUtils(QObject):
|
|||
# TODO move this to helper
|
||||
if rgame.game.supports_cloud_saves and not offline:
|
||||
try:
|
||||
sync = self.cloud_save_utils.sync_before_launch_game(rgame.app_name)
|
||||
sync = self.cloud_save_utils.sync_before_launch_game(rgame)
|
||||
except ValueError:
|
||||
logger.info("Cancel startup")
|
||||
self.sync_finished(rgame.app_name)
|
||||
self.sync_finished(rgame)
|
||||
return
|
||||
except AssertionError:
|
||||
dont_sync_after_finish = True
|
||||
else:
|
||||
if sync:
|
||||
self.launch_queue[rgame.app_name] = (rgame.app_name, skip_update_check, offline)
|
||||
self.launch_queue[rgame.app_name] = (rgame, skip_update_check, offline)
|
||||
return
|
||||
self.sync_finished(rgame.app_name)
|
||||
self.sync_finished(rgame)
|
||||
|
||||
self.launch_game(
|
||||
rgame, offline, skip_update_check, ask_always_sync=dont_sync_after_finish
|
||||
|
@ -284,18 +282,17 @@ class GameUtils(QObject):
|
|||
game_process.game_launched.connect(self.game_launched.emit)
|
||||
self.running_games[rgame.app_name] = game_process
|
||||
|
||||
def game_finished(self, app_name, exit_code):
|
||||
if self.running_games.get(app_name):
|
||||
self.running_games.pop(app_name)
|
||||
def game_finished(self, rgame: RareGame, exit_code):
|
||||
if self.running_games.get(rgame.app_name):
|
||||
self.running_games.pop(rgame.app_name)
|
||||
if exit_code == -1234:
|
||||
return
|
||||
|
||||
self.finished.emit(app_name, "")
|
||||
self.finished.emit(rgame.app_name, "")
|
||||
|
||||
logger.info(f"Game exited with exit code: {exit_code}")
|
||||
self.signals.discord_rpc.set_title.emit("")
|
||||
is_origin = self.core.get_game(app_name).third_party_store == "Origin"
|
||||
if exit_code == 1 and is_origin:
|
||||
if exit_code == 1 and rgame.is_origin:
|
||||
msg_box = QMessageBox()
|
||||
msg_box.setText(
|
||||
self.tr(
|
||||
|
@ -322,10 +319,10 @@ class GameUtils(QObject):
|
|||
)
|
||||
"""
|
||||
|
||||
if app_name in self.running_games.keys():
|
||||
self.running_games.pop(app_name)
|
||||
if rgame.app_name in self.running_games.keys():
|
||||
self.running_games.pop(rgame.app_name)
|
||||
|
||||
if self.core.get_game(app_name).supports_cloud_saves:
|
||||
if rgame.game.supports_cloud_saves:
|
||||
if exit_code != 0:
|
||||
r = QMessageBox.question(
|
||||
None,
|
||||
|
@ -341,13 +338,14 @@ class GameUtils(QObject):
|
|||
return
|
||||
|
||||
# TODO move this to helper
|
||||
self.cloud_save_utils.game_finished(app_name, always_ask=False)
|
||||
self.cloud_save_utils.game_finished(rgame, always_ask=False)
|
||||
|
||||
def sync_finished(self, app_name):
|
||||
if app_name in self.launch_queue.keys():
|
||||
self.cloud_save_finished.emit(app_name)
|
||||
params = self.launch_queue[app_name]
|
||||
self.launch_queue.pop(app_name)
|
||||
@pyqtSlot(RareGame)
|
||||
def sync_finished(self, rgame: RareGame):
|
||||
if rgame.app_name in self.launch_queue.keys():
|
||||
self.cloud_save_finished.emit(rgame.app_name)
|
||||
params = self.launch_queue[rgame.app_name]
|
||||
self.launch_queue.pop(rgame.app_name)
|
||||
self.launch_game(*params)
|
||||
else:
|
||||
self.cloud_save_finished.emit(app_name)
|
||||
self.cloud_save_finished.emit(rgame.app_name)
|
||||
|
|
Loading…
Reference in a new issue