1
0
Fork 0
mirror of synced 2024-06-26 18:20:50 +12:00

GameInfo: Use RareGame to keep the VerifyWorker running on a game and connect progress to the widget.

Signed-off-by: loathingKernel <142770+loathingKernel@users.noreply.github.com>
This commit is contained in:
loathingKernel 2023-01-06 21:55:41 +02:00
parent 2923c09bad
commit db0724fba0
3 changed files with 113 additions and 78 deletions

View file

@ -59,7 +59,6 @@ class GameInfo(QWidget):
self.rgame: Optional[RareGame] = None
self.game: Optional[Game] = None
self.igame: Optional[InstalledGame] = None
self.verify_threads: Dict[str, QRunnable] = {}
self.image = ImageWidget(self)
self.image.setFixedSize(ImageSize.Display)
@ -127,99 +126,95 @@ class GameInfo(QWidget):
@pyqtSlot()
def repair(self):
""" This function is to be called from the button only """
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f"{self.igame.app_name}.repair")
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f"{self.rgame.app_name}.repair")
if not os.path.exists(repair_file):
QMessageBox.warning(
self,
self.tr("Error - {}").format(self.igame.title),
self.tr("Error - {}").format(self.rgame.title),
self.tr(
"Repair file does not exist or game does not need a repair. Please verify game first"
),
)
return
self.repair_game(self.igame)
self.repair_game(self.rgame)
def repair_game(self, igame: InstalledGame):
game = self.core.get_game(igame.app_name)
def repair_game(self, rgame: RareGame):
rgame.update_game()
ans = False
if igame.version != game.app_version(igame.platform):
if rgame.has_update:
ans = QMessageBox.question(
self,
self.tr("Repair and update?"),
self.tr(
"There is an update for <b>{}</b> from <b>{}</b> to <b>{}</b>. "
"Do you want to update the game while repairing it?"
).format(igame.title, igame.version, game.app_version(igame.platform)),
).format(rgame.title, rgame.version, rgame.remote_version),
) == QMessageBox.Yes
self.signals.game.install.emit(
InstallOptionsModel(
app_name=igame.app_name, repair_mode=True, repair_and_update=ans, update=True
app_name=rgame.app_name, repair_mode=True, repair_and_update=ans, update=True
)
)
@pyqtSlot()
def verify(self):
""" This function is to be called from the button only """
if not os.path.exists(self.igame.install_path):
logger.error(f"Installation path {self.igame.install_path} for {self.igame.title} does not exist")
if not os.path.exists(self.rgame.igame.install_path):
logger.error(f"Installation path {self.rgame.igame.install_path} for {self.rgame.title} does not exist")
QMessageBox.warning(
self,
self.tr("Error - {}").format(self.igame.title),
self.tr("Installation path for <b>{}</b> does not exist. Cannot continue.").format(self.igame.title),
self.tr("Error - {}").format(self.rgame.title),
self.tr("Installation path for <b>{}</b> does not exist. Cannot continue.").format(self.rgame.title),
)
return
self.verify_game(self.igame)
self.verify_game(self.rgame)
def verify_game(self, igame: InstalledGame):
def verify_game(self, rgame: RareGame):
self.ui.verify_widget.setCurrentIndex(1)
verify_worker = VerifyWorker(igame.app_name)
verify_worker = VerifyWorker(rgame, self.core, self.args)
verify_worker.signals.status.connect(self.verify_status)
verify_worker.signals.result.connect(self.verify_result)
verify_worker.signals.error.connect(self.verify_error)
self.ui.verify_progress.setValue(0)
self.verify_threads[igame.app_name] = verify_worker
self.rgame.active_thread = verify_worker
self.verify_pool.start(verify_worker)
self.ui.move_button.setEnabled(False)
def verify_cleanup(self, app_name: str):
def verify_cleanup(self, rgame: RareGame):
self.ui.verify_widget.setCurrentIndex(0)
self.verify_threads.pop(app_name)
rgame.active_thread = None
self.ui.move_button.setEnabled(True)
self.ui.verify_button.setEnabled(True)
@pyqtSlot(str, str)
def verify_error(self, app_name, message):
self.verify_cleanup(app_name)
igame = self.core.get_installed_game(app_name)
@pyqtSlot(RareGame, str)
def verify_error(self, rgame: RareGame, message):
self.verify_cleanup(rgame)
QMessageBox.warning(
self,
self.tr("Error - {}").format(igame.title),
self.tr("Error - {}").format(rgame.title),
message
)
@pyqtSlot(str, int, int, float, float)
def verify_status(self, app_name, num, total, percentage, speed):
# checked, max, app_name
if app_name == self.game.app_name:
self.ui.verify_progress.setValue(num * 100 // total)
@pyqtSlot(RareGame, int, int, float, float)
def verify_status(self, rgame: RareGame, num, total, percentage, speed):
self.ui.verify_progress.setValue(num * 100 // total)
@pyqtSlot(str, bool, int, int)
def verify_result(self, app_name, success, failed, missing):
self.verify_cleanup(app_name)
@pyqtSlot(RareGame, bool, int, int)
def verify_result(self, rgame: RareGame, success, failed, missing):
self.verify_cleanup(rgame)
self.ui.repair_button.setDisabled(success)
igame = self.core.get_installed_game(app_name)
if success:
QMessageBox.information(
self,
self.tr("Summary - {}").format(igame.title),
self.tr("Summary - {}").format(rgame.title),
self.tr("<b>{}</b> has been verified successfully. "
"No missing or corrupt files found").format(igame.title),
"No missing or corrupt files found").format(rgame.title),
)
self.verification_finished.emit(igame)
self.verification_finished.emit(rgame.igame)
else:
ans = QMessageBox.question(
self,
self.tr("Summary - {}").format(igame.title),
self.tr("Summary - {}").format(rgame.title),
self.tr(
"Verification failed, <b>{}</b> file(s) corrupted, <b>{}</b> file(s) are missing. "
"Do you want to repair them?"
@ -228,7 +223,7 @@ class GameInfo(QWidget):
QMessageBox.Yes,
)
if ans == QMessageBox.Yes:
self.repair_game(igame)
self.repair_game(rgame)
@pyqtSlot(str)
def move_game(self, dest_path):
@ -325,8 +320,22 @@ class GameInfo(QWidget):
self.ui.move_button.showMenu()
def update_game(self, rgame: RareGame):
# FIXME: Use RareGame for the rest of the code
if self.rgame is not None:
if worker:= self.rgame.active_thread is not None:
if isinstance(worker, VerifyWorker):
try:
worker.signals.status.disconnect(self.verify_status)
except TypeError as e:
logger.warning(f"{self.rgame.app_title} verify worker: {e}")
self.rgame = rgame
if worker:= self.rgame.active_thread is not None:
if isinstance(worker, VerifyWorker):
self.ui.verify_widget.setCurrentIndex(1)
self.ui.verify_progress.setValue(self.rgame.progress)
worker.signals.status.connect(self.verify_status)
else:
self.ui.verify_widget.setCurrentIndex(0)
# FIXME: Use RareGame for the rest of the code
self.game = rgame.game
self.igame = rgame.igame
self.title.setTitle(self.game.app_title)
@ -386,17 +395,17 @@ class GameInfo(QWidget):
self.steam_worker.set_app_name(self.game.app_name)
QThreadPool.globalInstance().start(self.steam_worker)
if len(self.verify_threads.keys()) == 0 or not self.verify_threads.get(self.game.app_name):
self.ui.verify_widget.setCurrentIndex(0)
elif self.verify_threads.get(self.game.app_name):
self.ui.verify_widget.setCurrentIndex(1)
self.ui.verify_progress.setValue(
int(
self.verify_threads[self.game.app_name].num
/ self.verify_threads[self.game.app_name].total
* 100
)
)
# if len(self.verify_threads.keys()) == 0 or not self.verify_threads.get(self.game.app_name):
# self.ui.verify_widget.setCurrentIndex(0)
# elif self.verify_threads.get(self.game.app_name):
# self.ui.verify_widget.setCurrentIndex(1)
# self.ui.verify_progress.setValue(
# int(
# self.verify_threads[self.game.app_name].num
# / self.verify_threads[self.game.app_name].total
# * 100
# )
# )
# If the game that is currently moving matches with the current app_name, we show the progressbar.
# Otherwhise, we show the move tool button.
@ -409,7 +418,7 @@ class GameInfo(QWidget):
self.ui.move_stack.setCurrentIndex(index)
# If a game is verifying or moving, disable both verify and moving buttons.
if len(self.verify_threads):
if rgame.active_thread is not None:
self.ui.verify_button.setEnabled(False)
self.ui.move_button.setEnabled(False)
if self.is_moving:

View file

@ -1,6 +1,6 @@
import json
import os
from dataclasses import dataclass
from dataclasses import dataclass, field
from datetime import datetime
from enum import IntEnum
from logging import getLogger
@ -18,18 +18,21 @@ from rare.utils.paths import data_dir
logger = getLogger("RareGame")
class RareGameState(IntEnum):
IDLE = 0,
RUNNING = 1,
class RareGame(QObject):
class State(IntEnum):
IDLE = 0
RUNNING = 1
DOWNLOADING = 2
VERIFYING = 3
MOVING = 4
@dataclass
class Metadata:
queued: bool = False
queue_pos: Optional[int] = None
last_played: Optional[datetime] = None
tags: List[str] = field(default_factory=list)
@classmethod
def from_dict(cls, data: Dict):
@ -37,6 +40,7 @@ class RareGame(QObject):
queued=data.get("queued", False),
queue_pos=data.get("queue_pos", None),
last_played=datetime.strptime(data.get("last_played", "None"), "%Y-%m-%dT%H:%M:%S.%f"),
tags=data.get("tags", []),
)
def as_dict(self):
@ -44,6 +48,7 @@ class RareGame(QObject):
queued=self.queued,
queue_pos=self.queue_pos,
last_played=self.last_played.strftime("%Y-%m-%dT%H:%M:%S.%f"),
tags=self.tags,
)
def __bool__(self):
@ -123,6 +128,18 @@ class RareGame(QObject):
with open(os.path.join(data_dir(), "game_meta.json"), "w") as metadata_json:
json.dump(metadata, metadata_json, indent=2)
def update_game(self):
self.game = self.core.get_game(
self.app_name, update_meta=True, platform=self.igame.platform if self.igame else "Windows"
)
def update_igame(self):
self.igame = self.core.get_installed_game(self.app_name)
def update_rgame(self):
self.update_igame()
self.update_game()
@property
def app_name(self) -> str:
return self.igame.app_name if self.igame is not None else self.game.app_name
@ -346,11 +363,6 @@ class RareGame(QObject):
"""
return not self.game.asset_infos
def install(self):
self.signals.game.install.emit(
InstallOptionsModel(app_name=self.game.app_name)
)
@property
def is_origin(self) -> bool:
return self.game.metadata.get("customAttributes", {}).get("ThirdPartyManagedApp", {}).get("value") == "Origin"
@ -387,3 +399,9 @@ class RareGame(QObject):
def finish_progress(self, fail: bool, miss: int, app: str):
self.set_installed(True)
self.signals.progress.finish.emit(fail)
def install(self):
self.signals.game.install.emit(
InstallOptionsModel(app_name=self.game.app_name)
)

View file

@ -1,41 +1,47 @@
import os
import sys
from argparse import Namespace
from logging import getLogger
from PyQt5.QtCore import pyqtSignal, QObject, QRunnable
from rare.lgndr.cli import LegendaryCLI
from rare.lgndr.core import LegendaryCore
from rare.lgndr.glue.arguments import LgndrVerifyGameArgs
from rare.lgndr.glue.monkeys import LgndrIndirectStatus
from rare.shared import LegendaryCoreSingleton, ArgumentsSingleton
from rare.models.game import RareGame
logger = getLogger("VerificationWorker")
class VerifyWorker(QRunnable):
class Signals(QObject):
status = pyqtSignal(str, int, int, float, float)
result = pyqtSignal(str, bool, int, int)
error = pyqtSignal(str, str)
status = pyqtSignal(RareGame, int, int, float, float)
result = pyqtSignal(RareGame, bool, int, int)
error = pyqtSignal(RareGame, str)
num: int = 0
total: int = 1 # set default to 1 to avoid DivisionByZero before it is initialized
def __init__(self, app_name):
def __init__(self, rgame: RareGame, core: LegendaryCore, args: Namespace):
sys.excepthook = sys.__excepthook__
super(VerifyWorker, self).__init__()
self.signals = VerifyWorker.Signals()
self.setAutoDelete(True)
self.core = LegendaryCoreSingleton()
self.args = ArgumentsSingleton()
self.app_name = app_name
self.core = core
self.args = args
self.rgame = rgame
def status_callback(self, num: int, total: int, percentage: float, speed: float):
self.signals.status.emit(self.app_name, num, total, percentage, speed)
self.rgame.signals.progress.update.emit(num * 100 // total)
self.signals.status.emit(self.rgame, num, total, percentage, speed)
def run(self):
self.rgame.signals.progress.start.emit()
cli = LegendaryCLI(self.core)
status = LgndrIndirectStatus()
args = LgndrVerifyGameArgs(
app_name=self.app_name, indirect_status=status, verify_stdout=self.status_callback
app_name=self.rgame.app_name, indirect_status=status, verify_stdout=self.status_callback
)
# lk: first pass, verify with the current manifest
@ -57,16 +63,18 @@ class VerifyWorker(QRunnable):
if result is None:
raise ValueError
except ValueError:
self.signals.error.emit(self.app_name, status.message)
self.rgame.signals.progress.finish.emit(True)
self.signals.error.emit(self.rgame, status.message)
return
success = result is not None and not any(result)
if success:
# lk: if verification was successful we delete the repair file and run the clean procedure
# lk: this could probably be cut down to what is relevant for this use-case and skip the `cli` call
igame = self.core.get_installed_game(self.app_name)
game = self.core.get_game(self.app_name, platform=igame.platform)
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f"{self.app_name}.repair")
cli.install_game_cleanup(game=game, igame=igame, repair_mode=True, repair_file=repair_file)
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f"{self.rgame.app_name}.repair")
cli.install_game_cleanup(game=self.rgame.game, igame=self.rgame.igame, repair_mode=True, repair_file=repair_file)
self.rgame.update_rgame()
self.rgame.signals.progress.finish.emit(False)
self.signals.result.emit(self.rgame, success, *result)
self.signals.result.emit(self.app_name, success, *result)