1
0
Fork 0
mirror of synced 2024-06-23 08:40:45 +12:00

Fix most errors

This commit is contained in:
Dummerle 2022-06-11 16:59:53 +02:00
parent 8a9ef67d2f
commit 37ae6e2b6e
No known key found for this signature in database
GPG key ID: AB68CC59CA39F2F1
6 changed files with 51 additions and 100 deletions

View file

@ -2,7 +2,7 @@ import datetime
import sys
from dataclasses import dataclass
from logging import getLogger
from typing import Union, List
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
@ -139,7 +139,7 @@ class CloudSaveUtils(QObject):
self.thread_pool = QThreadPool.globalInstance()
def get_latest_saves(self, saves: List[SaveGameFile]) -> dict:
def get_latest_saves(self, saves: List[SaveGameFile]) -> Dict[str, SaveGameFile]:
save_games = set()
for igame in self.core.get_installed_list():
game = self.core.get_game(igame.app_name)

View file

@ -9,7 +9,6 @@ from PyQt5.QtCore import QObject, QSettings, QProcess, QProcessEnvironment, pyqt
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtNetwork import QLocalSocket
from PyQt5.QtWidgets import QMessageBox, QPushButton
from legendary.models.game import LaunchParameters, InstalledGame
from rare.components.dialogs.uninstall_dialog import UninstallDialog
from rare.components.extra.console import Console
@ -25,9 +24,10 @@ logger = getLogger("GameUtils")
class GameProcess(QObject):
game_finished = pyqtSignal(int, str) # exit_code, appname
game_launched = pyqtSignal(str)
tried_connections = 0
def __init__(self, app_name: str, on_startup=False):
def __init__(self, app_name: str, on_startup=False, always_ask_sync: bool= False):
super(GameProcess, self).__init__()
self.app_name = app_name
self.on_startup = on_startup
@ -36,6 +36,7 @@ class GameProcess(QObject):
self.socket.connected.connect(self._socket_connected)
self.socket.errorOccurred.connect(self._error_occurred)
self.socket.readyRead.connect(self._message_available)
self.always_ask_sync = always_ask_sync
def close_socket():
try:
@ -80,32 +81,38 @@ class GameProcess(QObject):
logger.error("Got unexpected action")
elif action == message_models.Actions.finished:
logger.info(f"{self.app_name} finished")
model = message_models.FinishedModel.base_from_json(data)
self._game_finished(model)
model = message_models.FinishedModel.from_json(data)
self.socket.close()
self._game_finished(model.exit_code)
elif action == message_models.Actions.error:
model = message_models.ErrorModel.from_json(data)
logger.error(f"Error in game {self.game.app_title}: {model.error_string}")
QMessageBox.warning(None, "Error", self.tr(
"Error in game {}: \n{}").format(self.game.app_title, model.error_string))
elif action == message_models.Actions.state_update:
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.app_name)
def _socket_connected(self):
self.timer.stop()
self.timer.deleteLater()
logger.info(f"Connection established for {self.app_name}")
if self.on_startup:
logger.info(f"Found {self.app_name} running at startup")
# FIXME run this after startup, widgets do not exist at this time
QTimer.singleShot(1000, lambda: self.game_launched.emit(self.app_name))
def _error_occurred(self, _):
if self.on_startup:
self.socket.close()
self.deleteLater()
self._game_finished(-1234)
self._game_finished(-1234) # 1234 is exit code for startup
logger.error(f"{self.app_name}: {self.socket.errorString()}")
def _game_finished(self, exit_code):
def _game_finished(self, exit_code: int):
self.game_finished.emit(exit_code, self.app_name)
@ -138,6 +145,7 @@ class GameUtils(QObject):
for igame in self.core.get_installed_list():
game_process = GameProcess(igame.app_name, True)
game_process.game_finished.connect(self.game_finished)
game_process.game_launched.connect(self.game_launched.emit)
self.running_games[igame.app_name] = game_process
def uninstall_game(self, app_name) -> bool:
@ -172,6 +180,7 @@ class GameUtils(QObject):
game = self.core.get_game(app_name)
dont_sync_after_finish = False
# TODO move this to helper
if game.supports_cloud_saves and not offline:
try:
sync = self.cloud_save_utils.sync_before_launch_game(app_name)
@ -216,10 +225,12 @@ class GameUtils(QObject):
if ask_always_sync:
args.extend("--ask-always-sync")
# kill me, if I don't change it before commit
QProcess.startDetached(executable, args)
logger.info(f"Start new Process: ({executable} {' '.join(args)})")
game_process = GameProcess(app_name)
game_process = GameProcess(app_name, ask_always_sync)
game_process.game_finished.connect(self.game_finished)
game_process.game_launched.connect(self.game_launched.emit)
self.running_games[app_name] = game_process
def game_finished(self, exit_code, app_name):
@ -346,76 +357,6 @@ class GameUtils(QObject):
command.append(origin_uri)
process.start(command[0], command[1:])
def _launch_game(self, igame: InstalledGame, process: QProcess, offline: bool,
skip_update_check: bool, ask_always_sync: bool):
if not offline: # skip for update
if not skip_update_check and not self.core.is_noupdate_game(igame.app_name):
# check updates
try:
latest = self.core.get_asset(
igame.app_name, igame.platform, update=False
)
except ValueError:
self.finished.emit(igame.app_name, self.tr("Metadata doesn't exist"))
return
else:
if latest.build_version != igame.version:
self.finished.emit(igame.app_name, self.tr("Please update game"))
return
params: LaunchParameters = self.core.get_launch_parameters(
app_name=igame.app_name, offline=offline
)
full_params = list()
if os.environ.get("container") == "flatpak":
full_params.extend(["flatpak-spawn", "--host"])
full_params.extend(params.launch_command)
full_params.append(
os.path.join(params.game_directory, params.game_executable)
)
full_params.extend(params.game_parameters)
full_params.extend(params.egl_parameters)
full_params.extend(params.user_parameters)
process.setWorkingDirectory(params.working_directory)
if platform.system() != "Windows":
# wine prefixes
for env in ["STEAM_COMPAT_DATA_PATH", "WINEPREFIX"]:
if val := process.processEnvironment().value(env, ""):
if not os.path.exists(val):
try:
os.makedirs(val)
except PermissionError as e:
logger.error(str(e))
QMessageBox.warning(
None,
"Error",
self.tr(
"Error while launching {}. No permission to create {} for {}"
).format(igame.title, val, env),
)
process.deleteLater()
return
# check wine executable
if shutil.which(full_params[0]) is None:
QMessageBox.warning(None, "Warning", self.tr("'{}' does not exist").format(full_params[0]))
return
running_game = RunningGameModel(
process=process, app_name=igame.app_name, always_ask_sync=ask_always_sync
)
process.start(full_params[0], full_params[1:])
self.game_launched.emit(igame.app_name)
self.signals.set_discord_rpc.emit(igame.app_name)
logger.info(f"{igame.title} launched")
self.running_games[igame.app_name] = running_game
def sync_finished(self, app_name):
if app_name in self.launch_queue.keys():
self.cloud_save_finished.emit(app_name)

View file

@ -80,14 +80,22 @@ class GameProcessHelper(QObject):
self.server.newConnection.connect(self.new_server_connection)
self.game_process.finished.connect(self.game_finished)
self.game_process.errorOccurred.connect(
lambda err: self.error_occurred(self.game_process.errorString()))
self.start_time = time.time()
def new_server_connection(self):
if self.socket is not None:
self.socket.disconnectFromServer()
try:
self.socket.disconnectFromServer()
except RuntimeError:
pass
self.logger.info("New connection")
self.socket = self.server.nextPendingConnection()
self.socket.disconnected.connect(self.socket.deleteLater)
self.socket.disconnected.connect(lambda: self.logger.info("Server disconnected"))
self.socket.flush()
def send_message(self, message: BaseModel):
@ -108,7 +116,7 @@ class GameProcessHelper(QObject):
)
)
self.exit_app.emit()
self.stop()
def launch_game(self, args: LaunchArgs):
# should never happen
@ -122,9 +130,9 @@ class GameProcessHelper(QObject):
QDesktopServices.openUrl(QUrl(args.executable))
return
self.game_process.finished.connect(self.game_finished)
self.game_process.errorOccurred.connect(
lambda err: self.error_occurred(self.game_process.errorString()))
if args.cwd:
self.game_process.setWorkingDirectory(args.cwd)
self.game_process.start(args.executable, args.args)
self.send_message(
StateChangedModel(
@ -159,6 +167,7 @@ class GameProcessHelper(QObject):
QThreadPool.globalInstance().start(worker)
def stop(self):
self.logger.info("Stopping server")
self.server.close()
self.server.deleteLater()
self.exit_app.emit()
@ -178,11 +187,14 @@ def start_game(args: Namespace):
def excepthook(exc_type, exc_value, exc_tb):
tb = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
helper.logger.fatal(tb)
helper.send_message(ErrorModel(
app_name=args.app_name,
action=Actions.error,
error_string=tb
))
try:
helper.send_message(ErrorModel(
app_name=args.app_name,
action=Actions.error,
error_string=tb
))
except RuntimeError:
pass
helper.stop()
sys.excepthook = excepthook
@ -196,4 +208,3 @@ def start_game(args: Namespace):
# quit_button.show()
# quit_button.clicked.connect(lambda: app.exit(0))
app.exec_()
helper.server.close()

View file

@ -39,6 +39,7 @@ class InitArgs:
class LaunchArgs:
executable: str = ""
args: List[str] = None
cwd: str = None
env: QProcessEnvironment = None
pre_launch_command: str = ""
pre_launch_wait: bool = False
@ -111,7 +112,7 @@ def get_game_params(core: LegendaryCore, igame: InstalledGame, args: InitArgs,
launch_args.env = QProcessEnvironment.systemEnvironment()
for name, value in params.environment.items():
launch_args.env.insert(name, value)
launch_args.cwd = params.working_directory
return launch_args

View file

@ -1,4 +1,3 @@
import enum
from dataclasses import dataclass
@ -14,9 +13,9 @@ class BaseModel:
action: int
app_name: str
@staticmethod
def base_from_json(data: dict) -> dict:
return dict(
@classmethod
def from_json(cls, data: dict):
return cls(
action=data["action"],
app_name=data["app_name"]
)
@ -30,7 +29,7 @@ class FinishedModel(BaseModel):
@classmethod
def from_json(cls, data):
return cls(
**super().base_from_json(data),
**BaseModel.from_json(data).__dict__,
exit_code=data["exit_code"],
playtime=data["playtime"],
)
@ -63,6 +62,6 @@ class ErrorModel(BaseModel):
@classmethod
def from_json(cls, data):
return cls(
**super().base_from_json(data),
**BaseModel.from_json(data).__dict__,
error_string=data["error_string"]
)

View file

@ -260,12 +260,11 @@ def get_rare_executable() -> List[str]:
executable = [sys.executable, os.path.abspath(sys.argv[0])]
elif platform.system() == "Windows":
executable = [sys.executable]
arguments = []
if not sys.executable.endswith("Rare.exe"):
# be sure to start consoleless then
executable[0] = executable[0].replace("python.exe", "pythonw.exe")
arguments.append(os.path.abspath(sys.argv[0]))
executable.extend(["-m", "rare"])
else: # macos not tested
executable = [sys.executable]