1
0
Fork 0
mirror of synced 2024-06-20 03:20:23 +12:00

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:
loathingKernel 2023-01-10 21:40:57 +02:00
parent 426be3e403
commit 7fbd941c98
4 changed files with 124 additions and 111 deletions

View file

@ -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
View 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)

View file

@ -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)