GameProcess: Move GameProcess out of GameUtils and into its own file under rare/shared
In the same vain, move `rare/game_launch_helper/message_models` into `rare/modes` since it is used in both the server and the client side. Signed-off-by: loathingKernel <142770+loathingKernel@users.noreply.github.com>
This commit is contained in:
parent
426be3e403
commit
7fbd941c98
|
@ -15,10 +15,10 @@ from PyQt5.QtNetwork import QLocalServer, QLocalSocket
|
|||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from rare.lgndr.core import LegendaryCore
|
||||
from rare.models.launcher import ErrorModel, Actions, FinishedModel, BaseModel, StateChangedModel
|
||||
from rare.widgets.rare_app import RareApp
|
||||
from .console import Console
|
||||
from .lgd_helper import get_launch_args, InitArgs, get_configured_process, LaunchArgs, GameArgsError
|
||||
from .message_models import ErrorModel, Actions, FinishedModel, BaseModel, StateChangedModel
|
||||
|
||||
logger = logging.getLogger("RareLauncher")
|
||||
|
||||
|
|
120
rare/shared/game_process.py
Normal file
120
rare/shared/game_process.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
import datetime
|
||||
import json
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, QTimer, pyqtSlot
|
||||
from PyQt5.QtNetwork import QLocalSocket
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from rare.models.game import RareGame
|
||||
from rare.models.launcher import ErrorModel, Actions, FinishedModel, StateChangedModel
|
||||
|
||||
logger = logging.getLogger("GameProcess")
|
||||
|
||||
|
||||
class GameProcess(QObject):
|
||||
# str: app_name, int: exit_code
|
||||
game_finished = pyqtSignal(RareGame, int)
|
||||
# str: app_name
|
||||
game_launched = pyqtSignal(RareGame)
|
||||
|
||||
def __init__(self, rgame: RareGame, on_startup=False, always_ask_sync: bool = False):
|
||||
super(GameProcess, self).__init__()
|
||||
self.rgame = rgame
|
||||
self.on_startup = on_startup
|
||||
self.tried_connections = 0
|
||||
self.socket = QLocalSocket()
|
||||
self.socket.connected.connect(self._socket_connected)
|
||||
try:
|
||||
self.socket.errorOccurred.connect(self._error_occurred)
|
||||
except AttributeError:
|
||||
QTimer.singleShot(100, lambda: self._error_occurred(QLocalSocket.UnknownSocketError) if self.socket.error() else None)
|
||||
logger.warning("Do not handle errors on QLocalSocket, because of an old qt version")
|
||||
self.socket.readyRead.connect(self._message_available)
|
||||
self.always_ask_sync = always_ask_sync
|
||||
|
||||
def close_socket():
|
||||
try:
|
||||
self.socket.close()
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
self.socket.disconnected.connect(close_socket)
|
||||
self.timer = QTimer()
|
||||
if not on_startup:
|
||||
# wait a short time for process started
|
||||
self.timer.timeout.connect(self.connect_to_server)
|
||||
self.timer.start(200)
|
||||
else:
|
||||
# nothing happens, if no server available
|
||||
self.connect_to_server()
|
||||
|
||||
def connect_to_server(self):
|
||||
self.socket.connectToServer(f"rare_{self.rgame.app_name}")
|
||||
self.tried_connections += 1
|
||||
|
||||
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, 1)
|
||||
|
||||
@pyqtSlot()
|
||||
def _message_available(self):
|
||||
message = self.socket.readAll().data()
|
||||
if not message.startswith(b"{"):
|
||||
logger.error(f"Received unsupported message: {message.decode('utf-8')}")
|
||||
return
|
||||
try:
|
||||
data = json.loads(message)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(e)
|
||||
logger.error("Could not load json data")
|
||||
return
|
||||
|
||||
action = data.get("action", -1)
|
||||
|
||||
if action == -1:
|
||||
logger.error("Got unexpected action")
|
||||
elif action == Actions.finished:
|
||||
logger.info(f"{self.rgame.app_name} {self.rgame.app_title} finished")
|
||||
model = FinishedModel.from_json(data)
|
||||
self.socket.close()
|
||||
self._game_finished(model.exit_code)
|
||||
elif action == Actions.error:
|
||||
model = ErrorModel.from_json(data)
|
||||
logger.error(f"Error in game {self.rgame.app_title}: {model.error_string}")
|
||||
self.socket.close()
|
||||
self._game_finished(1)
|
||||
QMessageBox.warning(None, "Error", self.tr(
|
||||
"Error in game {}: \n{}").format(self.rgame.app_title, model.error_string))
|
||||
|
||||
elif action == Actions.state_update:
|
||||
model = StateChangedModel.from_json(data)
|
||||
if model.new_state == StateChangedModel.States.started:
|
||||
logger.info("Launched Game")
|
||||
self.game_launched.emit(self.rgame)
|
||||
meta_data = self.rgame.metadata
|
||||
meta_data.last_played = datetime.datetime.now()
|
||||
self.rgame.save_metadata()
|
||||
|
||||
@pyqtSlot()
|
||||
def _socket_connected(self):
|
||||
self.timer.stop()
|
||||
self.timer.deleteLater()
|
||||
logger.info(f"Connection established for {self.rgame.app_name} ({self.rgame.app_title})")
|
||||
if self.on_startup:
|
||||
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))
|
||||
|
||||
@pyqtSlot(QLocalSocket.LocalSocketError)
|
||||
def _error_occurred(self, _: QLocalSocket.LocalSocketError):
|
||||
if self.on_startup:
|
||||
self.socket.close()
|
||||
self._game_finished(-1234) # 1234 is exit code for startup
|
||||
logger.error(f"{self.rgame.app_name} ({self.rgame.app_title}): {self.socket.errorString()}")
|
||||
|
||||
def _game_finished(self, exit_code: int):
|
||||
self.deleteLater()
|
||||
self.game_finished.emit(self.rgame, exit_code)
|
|
@ -1,134 +1,26 @@
|
|||
import datetime
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import QObject, QProcess, pyqtSignal, QUrl, QTimer, pyqtSlot
|
||||
from PyQt5.QtCore import QObject, QProcess, pyqtSignal, QUrl, pyqtSlot
|
||||
from PyQt5.QtCore import QStandardPaths
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
from PyQt5.QtNetwork import QLocalSocket
|
||||
from PyQt5.QtWidgets import QMessageBox, QPushButton
|
||||
from legendary.core import LegendaryCore
|
||||
|
||||
from rare.components.dialogs.uninstall_dialog import UninstallDialog
|
||||
from rare.game_launch_helper import message_models
|
||||
from rare.lgndr.cli import LegendaryCLI
|
||||
from rare.lgndr.glue.arguments import LgndrUninstallGameArgs
|
||||
from rare.lgndr.glue.monkeys import LgndrIndirectStatus
|
||||
from rare.models.game import RareGame
|
||||
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
|
||||
from rare.shared.game_process import GameProcess
|
||||
from rare.utils import config_helper, misc
|
||||
from .cloud_save_utils import CloudSaveUtils
|
||||
|
||||
logger = getLogger("GameUtils")
|
||||
|
||||
|
||||
class GameProcess(QObject):
|
||||
# str: app_name, int: exit_code
|
||||
game_finished = pyqtSignal(RareGame, int)
|
||||
# str: app_name
|
||||
game_launched = pyqtSignal(RareGame)
|
||||
|
||||
def __init__(self, rgame: RareGame, on_startup=False, always_ask_sync: bool = False):
|
||||
super(GameProcess, self).__init__()
|
||||
self.rgame = rgame
|
||||
self.on_startup = on_startup
|
||||
self.tried_connections = 0
|
||||
self.socket = QLocalSocket()
|
||||
self.socket.connected.connect(self._socket_connected)
|
||||
try:
|
||||
self.socket.errorOccurred.connect(self._error_occurred)
|
||||
except AttributeError:
|
||||
QTimer.singleShot(100, lambda: self._error_occurred(None) if self.socket.error() else None)
|
||||
logger.warning("Do not handle errors on QLocalSocket, because of an old qt version")
|
||||
self.socket.readyRead.connect(self._message_available)
|
||||
self.always_ask_sync = always_ask_sync
|
||||
|
||||
def close_socket():
|
||||
try:
|
||||
self.socket.close()
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
self.socket.disconnected.connect(close_socket)
|
||||
self.timer = QTimer()
|
||||
if not on_startup:
|
||||
# wait a short time for process started
|
||||
self.timer.timeout.connect(self.connect_to_server)
|
||||
self.timer.start(200)
|
||||
else:
|
||||
# nothing happens, if no server available
|
||||
self.connect_to_server()
|
||||
|
||||
def connect_to_server(self):
|
||||
self.socket.connectToServer(f"rare_{self.rgame.app_name}")
|
||||
self.tried_connections += 1
|
||||
|
||||
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, 1)
|
||||
|
||||
def _message_available(self):
|
||||
message = self.socket.readAll().data()
|
||||
if not message.startswith(b"{"):
|
||||
logger.error(f"Received unsupported message: {message.decode('utf-8')}")
|
||||
return
|
||||
try:
|
||||
data = json.loads(message)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(e)
|
||||
logger.error("Could not load json data")
|
||||
return
|
||||
|
||||
action = data.get("action", -1)
|
||||
|
||||
if action == -1:
|
||||
logger.error("Got unexpected action")
|
||||
elif action == message_models.Actions.finished:
|
||||
logger.info(f"{self.rgame.app_name} {self.rgame.app_title} finished")
|
||||
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.rgame.app_title}: {model.error_string}")
|
||||
self.socket.close()
|
||||
self._game_finished(1)
|
||||
QMessageBox.warning(None, "Error", self.tr(
|
||||
"Error in game {}: \n{}").format(self.rgame.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.rgame)
|
||||
meta_data = self.rgame.metadata
|
||||
meta_data.last_played = datetime.datetime.now()
|
||||
self.rgame.save_metadata()
|
||||
|
||||
def _socket_connected(self):
|
||||
self.timer.stop()
|
||||
self.timer.deleteLater()
|
||||
logger.info(f"Connection established for {self.rgame.app_name} ({self.rgame.app_title})")
|
||||
if self.on_startup:
|
||||
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))
|
||||
|
||||
def _error_occurred(self, _):
|
||||
if self.on_startup:
|
||||
self.socket.close()
|
||||
self.deleteLater()
|
||||
self._game_finished(-1234) # 1234 is exit code for startup
|
||||
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, exit_code)
|
||||
|
||||
|
||||
def uninstall_game(core: LegendaryCore, app_name: str, keep_files=False, keep_config=False):
|
||||
igame = core.get_installed_game(app_name)
|
||||
|
||||
|
@ -282,6 +174,7 @@ class GameUtils(QObject):
|
|||
game_process.game_launched.connect(self.game_launched.emit)
|
||||
self.running_games[rgame.app_name] = game_process
|
||||
|
||||
@pyqtSlot(RareGame, int)
|
||||
def game_finished(self, rgame: RareGame, exit_code):
|
||||
if self.running_games.get(rgame.app_name):
|
||||
self.running_games.pop(rgame.app_name)
|
||||
|
|
Loading…
Reference in a new issue