Rare: Move the different applications into their respective folders
This commit is contained in:
parent
5f062830cf
commit
bdbb61d3a6
|
@ -1,2 +1,10 @@
|
||||||
__version__ = "1.10.7"
|
__version__ = "1.10.7"
|
||||||
__codename__ = "Garlic Crab"
|
__codename__ = "Garlic Crab"
|
||||||
|
|
||||||
|
# For PyCharm profiler
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
from argparse import Namespace
|
||||||
|
from rare import client
|
||||||
|
status = client.start(Namespace(debug=True, silent=False, offline=False, test_start=False))
|
||||||
|
sys.exit(status)
|
||||||
|
|
110
rare/__main__.py
110
rare/__main__.py
|
@ -1,114 +1,10 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
import multiprocessing
|
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
from argparse import ArgumentParser
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# fix cx_freeze
|
|
||||||
multiprocessing.freeze_support()
|
|
||||||
|
|
||||||
# insert legendary for installed via pip/setup.py submodule to path
|
|
||||||
# if not __name__ == "__main__":
|
|
||||||
# sys.path.insert(0, os.path.join(os.path.dirname(__file__), "legendary"))
|
|
||||||
|
|
||||||
# CLI Options
|
|
||||||
parser = ArgumentParser()
|
|
||||||
parser.add_argument(
|
|
||||||
"-V", "--version", action="store_true", help="Shows version and exits"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-S",
|
|
||||||
"--silent",
|
|
||||||
action="store_true",
|
|
||||||
help="Launch Rare in background. Open it from System Tray Icon",
|
|
||||||
)
|
|
||||||
parser.add_argument("--debug", action="store_true", help="Launch in debug mode")
|
|
||||||
parser.add_argument(
|
|
||||||
"--offline", action="store_true", help="Launch Rare in offline mode"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--test-start", action="store_true", help="Quit immediately after launch"
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--desktop-shortcut",
|
|
||||||
action="store_true",
|
|
||||||
dest="desktop_shortcut",
|
|
||||||
help="Use this, if there is no link on desktop to start Rare",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--startmenu-shortcut",
|
|
||||||
action="store_true",
|
|
||||||
dest="startmenu_shortcut",
|
|
||||||
help="Use this, if there is no link in start menu to launch Rare",
|
|
||||||
)
|
|
||||||
subparsers = parser.add_subparsers(title="Commands", dest="subparser")
|
|
||||||
|
|
||||||
launch_minimal_parser = subparsers.add_parser("start", aliases=["launch"])
|
|
||||||
launch_minimal_parser.add_argument("app_name", help="AppName of the game to launch",
|
|
||||||
metavar="<App Name>", action="store")
|
|
||||||
launch_minimal_parser.add_argument("--dry-run", help="Print arguments and exit", action="store_true")
|
|
||||||
launch_minimal_parser.add_argument("--offline", help="Launch game offline",
|
|
||||||
action="store_true")
|
|
||||||
launch_minimal_parser.add_argument('--wine-bin', dest='wine_bin', action='store', metavar='<wine binary>',
|
|
||||||
default=os.environ.get('LGDRY_WINE_BINARY', None),
|
|
||||||
help='Set WINE binary to use to launch the app')
|
|
||||||
launch_minimal_parser.add_argument('--wine-prefix', dest='wine_pfx', action='store', metavar='<wine pfx path>',
|
|
||||||
default=os.environ.get('LGDRY_WINE_PREFIX', None),
|
|
||||||
help='Set WINE prefix to use')
|
|
||||||
launch_minimal_parser.add_argument("--ask-sync-saves", help="Ask to sync cloud saves",
|
|
||||||
action="store_true")
|
|
||||||
launch_minimal_parser.add_argument("--skip-update-check", help="Do not check for updates",
|
|
||||||
action="store_true")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if args.desktop_shortcut or args.startmenu_shortcut:
|
|
||||||
from rare.utils.paths import create_desktop_link
|
|
||||||
|
|
||||||
if args.desktop_shortcut:
|
|
||||||
create_desktop_link(app_name="rare_shortcut", link_type="desktop")
|
|
||||||
|
|
||||||
if args.startmenu_shortcut:
|
|
||||||
create_desktop_link(app_name="rare_shortcut", link_type="start_menu")
|
|
||||||
|
|
||||||
print("Link created")
|
|
||||||
return
|
|
||||||
|
|
||||||
if args.version:
|
|
||||||
from rare import __version__, code_name
|
|
||||||
print(f"Rare {__version__} Codename: {code_name}")
|
|
||||||
return
|
|
||||||
|
|
||||||
if args.subparser == "start" or args.subparser == "launch":
|
|
||||||
from rare import game_launch_helper as helper
|
|
||||||
helper.start_game(args)
|
|
||||||
return
|
|
||||||
|
|
||||||
from rare.utils import singleton
|
|
||||||
|
|
||||||
try:
|
|
||||||
# this object only allows one instance per machine
|
|
||||||
|
|
||||||
me = singleton.SingleInstance()
|
|
||||||
except singleton.SingleInstanceException:
|
|
||||||
print("Rare is already running")
|
|
||||||
from rare.utils.paths import lock_file
|
|
||||||
|
|
||||||
with open(lock_file(), "w") as file:
|
|
||||||
file.write("show")
|
|
||||||
file.close()
|
|
||||||
return
|
|
||||||
|
|
||||||
from rare.app import start
|
|
||||||
|
|
||||||
start(args)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
from rare.main import main
|
||||||
|
|
||||||
# run from source
|
# run from source
|
||||||
# insert raw legendary submodule
|
# insert raw legendary submodule
|
||||||
# sys.path.insert(
|
# sys.path.insert(
|
||||||
|
@ -130,4 +26,4 @@ if __name__ == "__main__":
|
||||||
if sys.stderr is None:
|
if sys.stderr is None:
|
||||||
sys.stderr = open(os.devnull, 'w')
|
sys.stderr = open(os.devnull, 'w')
|
||||||
|
|
||||||
main()
|
sys.exit(main())
|
||||||
|
|
132
rare/app.py
132
rare/app.py
|
@ -1,132 +0,0 @@
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import sys
|
|
||||||
import traceback
|
|
||||||
from argparse import Namespace
|
|
||||||
from datetime import datetime, timezone
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
import requests.exceptions
|
|
||||||
from PyQt5.QtCore import QThreadPool, QTimer, pyqtSlot, Qt
|
|
||||||
from PyQt5.QtWidgets import QApplication, QMessageBox
|
|
||||||
from requests import HTTPError
|
|
||||||
|
|
||||||
from rare.components.dialogs.launch_dialog import LaunchDialog
|
|
||||||
from rare.components.main_window import MainWindow
|
|
||||||
from rare.shared import RareCore
|
|
||||||
from rare.utils import config_helper, paths
|
|
||||||
from rare.widgets.rare_app import RareApp, RareAppException
|
|
||||||
|
|
||||||
logger = logging.getLogger("Rare")
|
|
||||||
|
|
||||||
|
|
||||||
class RareException(RareAppException):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super(RareException, self).__init__(parent=parent)
|
|
||||||
|
|
||||||
def _handler(self, exc_type, exc_value, exc_tb) -> bool:
|
|
||||||
if exc_type == HTTPError:
|
|
||||||
try:
|
|
||||||
if RareCore.instance() is not None:
|
|
||||||
if RareCore.instance().core().login():
|
|
||||||
return True
|
|
||||||
raise ValueError
|
|
||||||
except Exception as e:
|
|
||||||
logger.fatal(str(e))
|
|
||||||
QMessageBox.warning(None, "Error", self.tr("Failed to login"))
|
|
||||||
QApplication.exit(1)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class Rare(RareApp):
|
|
||||||
def __init__(self, args: Namespace):
|
|
||||||
log_file = "Rare_{0}.log"
|
|
||||||
super(Rare, self).__init__(args, log_file)
|
|
||||||
self._hook.deleteLater()
|
|
||||||
self._hook = RareException(self)
|
|
||||||
self.rcore = RareCore(args=args)
|
|
||||||
self.args = RareCore.instance().args()
|
|
||||||
self.signals = RareCore.instance().signals()
|
|
||||||
self.core = RareCore.instance().core()
|
|
||||||
|
|
||||||
config_helper.init_config_handler(self.core)
|
|
||||||
|
|
||||||
lang = self.settings.value("language", self.core.language_code, type=str)
|
|
||||||
self.load_translator(lang)
|
|
||||||
|
|
||||||
# set Application name for settings
|
|
||||||
self.main_window: Optional[MainWindow] = None
|
|
||||||
self.launch_dialog: Optional[LaunchDialog] = None
|
|
||||||
self.timer: Optional[QTimer] = None
|
|
||||||
|
|
||||||
# launch app
|
|
||||||
self.launch_dialog = LaunchDialog(parent=None)
|
|
||||||
self.launch_dialog.quit_app.connect(self.launch_dialog.close)
|
|
||||||
self.launch_dialog.quit_app.connect(lambda x: sys.exit(x))
|
|
||||||
self.launch_dialog.start_app.connect(self.start_app)
|
|
||||||
self.launch_dialog.start_app.connect(self.launch_dialog.close)
|
|
||||||
|
|
||||||
self.launch_dialog.login()
|
|
||||||
|
|
||||||
def poke_timer(self):
|
|
||||||
dt_exp = datetime.fromisoformat(self.core.lgd.userdata['expires_at'][:-1]).replace(tzinfo=timezone.utc)
|
|
||||||
dt_now = datetime.utcnow().replace(tzinfo=timezone.utc)
|
|
||||||
td = abs(dt_exp - dt_now)
|
|
||||||
self.timer.start(int(td.total_seconds() - 60) * 1000)
|
|
||||||
logger.info(f"Renewed session expires at {self.core.lgd.userdata['expires_at']}")
|
|
||||||
|
|
||||||
def re_login(self):
|
|
||||||
logger.info("Session expires shortly. Renew session")
|
|
||||||
try:
|
|
||||||
self.core.login()
|
|
||||||
except requests.exceptions.ConnectionError:
|
|
||||||
self.timer.start(3000) # try again if no connection
|
|
||||||
return
|
|
||||||
self.poke_timer()
|
|
||||||
|
|
||||||
def start_app(self):
|
|
||||||
self.timer = QTimer()
|
|
||||||
self.timer.timeout.connect(self.re_login)
|
|
||||||
self.poke_timer()
|
|
||||||
|
|
||||||
self.main_window = MainWindow()
|
|
||||||
self.main_window.exit_app.connect(self.on_exit_app)
|
|
||||||
|
|
||||||
if not self.args.silent:
|
|
||||||
self.main_window.show()
|
|
||||||
|
|
||||||
if self.args.test_start:
|
|
||||||
self.main_window.close()
|
|
||||||
self.main_window = None
|
|
||||||
self.on_exit_app(0)
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
@pyqtSlot(int)
|
|
||||||
def on_exit_app(self, exit_code=0):
|
|
||||||
threadpool = QThreadPool.globalInstance()
|
|
||||||
threadpool.waitForDone()
|
|
||||||
if self.timer is not None:
|
|
||||||
self.timer.stop()
|
|
||||||
self.timer.deleteLater()
|
|
||||||
self.timer = None
|
|
||||||
self.rcore.deleteLater()
|
|
||||||
del self.rcore
|
|
||||||
self.processEvents()
|
|
||||||
shutil.rmtree(paths.tmp_dir())
|
|
||||||
os.makedirs(paths.tmp_dir())
|
|
||||||
|
|
||||||
self.exit(exit_code)
|
|
||||||
|
|
||||||
|
|
||||||
def start(args):
|
|
||||||
while True:
|
|
||||||
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
|
||||||
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
|
||||||
app = Rare(args)
|
|
||||||
exit_code = app.exec_()
|
|
||||||
# if not restart
|
|
||||||
# restart app
|
|
||||||
del app
|
|
||||||
if exit_code != -133742:
|
|
||||||
break
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from argparse import Namespace
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import requests.exceptions
|
||||||
|
from PyQt5.QtCore import QThreadPool, QTimer, pyqtSlot, Qt
|
||||||
|
from PyQt5.QtWidgets import QApplication, QMessageBox
|
||||||
|
from requests import HTTPError
|
||||||
|
|
||||||
|
from rare.components.dialogs.launch_dialog import LaunchDialog
|
||||||
|
from rare.components.main_window import MainWindow
|
||||||
|
from rare.shared import RareCore
|
||||||
|
from rare.utils import config_helper, paths
|
||||||
|
from rare.widgets.rare_app import RareApp, RareAppException
|
||||||
|
|
||||||
|
|
||||||
|
class RareException(RareAppException):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(RareException, self).__init__(parent=parent)
|
||||||
|
|
||||||
|
def _handler(self, exc_type, exc_value, exc_tb) -> bool:
|
||||||
|
if exc_type == HTTPError:
|
||||||
|
try:
|
||||||
|
if RareCore.instance() is not None:
|
||||||
|
if RareCore.instance().core().login():
|
||||||
|
return True
|
||||||
|
raise ValueError
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.fatal(str(e))
|
||||||
|
QMessageBox.warning(None, "Error", self.tr("Failed to login"))
|
||||||
|
QApplication.exit(1)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Rare(RareApp):
|
||||||
|
def __init__(self, args: Namespace):
|
||||||
|
super(Rare, self).__init__(args, f"{type(self).__name__}_{{0}}.log")
|
||||||
|
self._hook.deleteLater()
|
||||||
|
self._hook = RareException(self)
|
||||||
|
self.rcore = RareCore(args=args)
|
||||||
|
self.args = RareCore.instance().args()
|
||||||
|
self.signals = RareCore.instance().signals()
|
||||||
|
self.core = RareCore.instance().core()
|
||||||
|
|
||||||
|
config_helper.init_config_handler(self.core)
|
||||||
|
|
||||||
|
lang = self.settings.value("language", self.core.language_code, type=str)
|
||||||
|
self.load_translator(lang)
|
||||||
|
|
||||||
|
# set Application name for settings
|
||||||
|
self.main_window: Optional[MainWindow] = None
|
||||||
|
self.launch_dialog: Optional[LaunchDialog] = None
|
||||||
|
self.timer: Optional[QTimer] = None
|
||||||
|
|
||||||
|
# launch app
|
||||||
|
self.launch_dialog = LaunchDialog(parent=None)
|
||||||
|
self.launch_dialog.quit_app.connect(self.launch_dialog.close)
|
||||||
|
self.launch_dialog.quit_app.connect(lambda x: sys.exit(x))
|
||||||
|
self.launch_dialog.start_app.connect(self.start_app)
|
||||||
|
self.launch_dialog.start_app.connect(self.launch_dialog.close)
|
||||||
|
|
||||||
|
self.launch_dialog.login()
|
||||||
|
|
||||||
|
def poke_timer(self):
|
||||||
|
dt_exp = datetime.fromisoformat(self.core.lgd.userdata['expires_at'][:-1]).replace(tzinfo=timezone.utc)
|
||||||
|
dt_now = datetime.utcnow().replace(tzinfo=timezone.utc)
|
||||||
|
td = abs(dt_exp - dt_now)
|
||||||
|
self.timer.start(int(td.total_seconds() - 60) * 1000)
|
||||||
|
self.logger.info(f"Renewed session expires at {self.core.lgd.userdata['expires_at']}")
|
||||||
|
|
||||||
|
def re_login(self):
|
||||||
|
self.logger.info("Session expires shortly. Renew session")
|
||||||
|
try:
|
||||||
|
self.core.login()
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
self.timer.start(3000) # try again if no connection
|
||||||
|
return
|
||||||
|
self.poke_timer()
|
||||||
|
|
||||||
|
def start_app(self):
|
||||||
|
self.timer = QTimer()
|
||||||
|
self.timer.timeout.connect(self.re_login)
|
||||||
|
self.poke_timer()
|
||||||
|
|
||||||
|
self.main_window = MainWindow()
|
||||||
|
self.main_window.exit_app.connect(self.on_exit_app)
|
||||||
|
|
||||||
|
if not self.args.silent:
|
||||||
|
self.main_window.show()
|
||||||
|
|
||||||
|
if self.args.test_start:
|
||||||
|
self.main_window.close()
|
||||||
|
self.main_window = None
|
||||||
|
self.on_exit_app(0)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def on_exit_app(self, exit_code=0):
|
||||||
|
threadpool = QThreadPool.globalInstance()
|
||||||
|
threadpool.waitForDone()
|
||||||
|
if self.timer is not None:
|
||||||
|
self.timer.stop()
|
||||||
|
self.timer.deleteLater()
|
||||||
|
self.timer = None
|
||||||
|
self.rcore.deleteLater()
|
||||||
|
del self.rcore
|
||||||
|
self.processEvents()
|
||||||
|
shutil.rmtree(paths.tmp_dir())
|
||||||
|
os.makedirs(paths.tmp_dir())
|
||||||
|
|
||||||
|
self.exit(exit_code)
|
||||||
|
|
||||||
|
|
||||||
|
def start(args) -> int:
|
||||||
|
while True:
|
||||||
|
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
||||||
|
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
||||||
|
app = Rare(args)
|
||||||
|
exit_code = app.exec_()
|
||||||
|
# if not restart
|
||||||
|
# restart app
|
||||||
|
del app
|
||||||
|
if exit_code != -133742:
|
||||||
|
break
|
||||||
|
return exit_code
|
|
@ -1,14 +1,12 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from signal import signal, SIGINT, SIGTERM, strsignal
|
from signal import signal, SIGINT, SIGTERM, strsignal
|
||||||
from typing import Union, Optional
|
from typing import Optional
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, QProcess, pyqtSignal, QUrl, QRunnable, QThreadPool, QSettings, Qt, pyqtSlot
|
from PyQt5.QtCore import QObject, QProcess, pyqtSignal, QUrl, QRunnable, QThreadPool, QSettings, Qt, pyqtSlot
|
||||||
from PyQt5.QtGui import QDesktopServices
|
from PyQt5.QtGui import QDesktopServices
|
||||||
|
@ -24,8 +22,6 @@ from rare.widgets.rare_app import RareApp, RareAppException
|
||||||
from .console import Console
|
from .console import Console
|
||||||
from .lgd_helper import get_launch_args, InitArgs, get_configured_process, LaunchArgs, GameArgsError
|
from .lgd_helper import get_launch_args, InitArgs, get_configured_process, LaunchArgs, GameArgsError
|
||||||
|
|
||||||
logger = logging.getLogger("RareLauncher")
|
|
||||||
|
|
||||||
DETACHED_APP_NAMES = [
|
DETACHED_APP_NAMES = [
|
||||||
"0a2d9f6403244d12969e11da6713137b"
|
"0a2d9f6403244d12969e11da6713137b"
|
||||||
]
|
]
|
||||||
|
@ -40,6 +36,7 @@ class PreLaunchThread(QRunnable):
|
||||||
|
|
||||||
def __init__(self, core: LegendaryCore, args: InitArgs, rgame: RareGameSlim, sync_action=None):
|
def __init__(self, core: LegendaryCore, args: InitArgs, rgame: RareGameSlim, sync_action=None):
|
||||||
super(PreLaunchThread, self).__init__()
|
super(PreLaunchThread, self).__init__()
|
||||||
|
self.logger = getLogger(type(self).__name__)
|
||||||
self.core = core
|
self.core = core
|
||||||
self.signals = self.Signals()
|
self.signals = self.Signals()
|
||||||
self.args = args
|
self.args = args
|
||||||
|
@ -47,13 +44,13 @@ class PreLaunchThread(QRunnable):
|
||||||
self.sync_action = sync_action
|
self.sync_action = sync_action
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
logger.info(f"Sync action: {self.sync_action}")
|
self.logger.info(f"Sync action: {self.sync_action}")
|
||||||
if self.sync_action == CloudSaveDialog.UPLOAD:
|
if self.sync_action == CloudSaveDialog.UPLOAD:
|
||||||
self.rgame.upload_saves(False)
|
self.rgame.upload_saves(False)
|
||||||
elif self.sync_action == CloudSaveDialog.DOWNLOAD:
|
elif self.sync_action == CloudSaveDialog.DOWNLOAD:
|
||||||
self.rgame.download_saves(False)
|
self.rgame.download_saves(False)
|
||||||
else:
|
else:
|
||||||
logger.info("No sync action")
|
self.logger.info("No sync action")
|
||||||
|
|
||||||
args = self.prepare_launch(self.args)
|
args = self.prepare_launch(self.args)
|
||||||
if not args:
|
if not args:
|
||||||
|
@ -121,11 +118,9 @@ class RareLauncher(RareApp):
|
||||||
exit_app = pyqtSignal()
|
exit_app = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, args: InitArgs):
|
def __init__(self, args: InitArgs):
|
||||||
log_file = f"Rare_Launcher_{args.app_name}" + "_{0}.log"
|
super(RareLauncher, self).__init__(args, f"{type(self).__name__}_{args.app_name}_{{0}}.log")
|
||||||
super(RareLauncher, self).__init__(args, log_file)
|
|
||||||
self._hook.deleteLater()
|
self._hook.deleteLater()
|
||||||
self._hook = RareLauncherException(self, args, self)
|
self._hook = RareLauncherException(self, args, self)
|
||||||
self.logger = getLogger(f"Launcher_{args.app_name}")
|
|
||||||
|
|
||||||
self.success: bool = True
|
self.success: bool = True
|
||||||
self.no_sync_on_exit = False
|
self.no_sync_on_exit = False
|
||||||
|
@ -280,7 +275,7 @@ class RareLauncher(RareApp):
|
||||||
self.stop()
|
self.stop()
|
||||||
return
|
return
|
||||||
if self.args.dry_run:
|
if self.args.dry_run:
|
||||||
logger.info("Dry run activated")
|
self.logger.info("Dry run activated")
|
||||||
if self.console:
|
if self.console:
|
||||||
self.console.log(f"{args.executable} {' '.join(args.args)}")
|
self.console.log(f"{args.executable} {' '.join(args.args)}")
|
||||||
self.console.log(f"Do not start {self.rgame.app_name}")
|
self.console.log(f"Do not start {self.rgame.app_name}")
|
||||||
|
@ -338,7 +333,7 @@ class RareLauncher(RareApp):
|
||||||
args.offline = True
|
args.offline = True
|
||||||
|
|
||||||
if not args.offline and self.rgame.auto_sync_saves:
|
if not args.offline and self.rgame.auto_sync_saves:
|
||||||
logger.info("Start sync worker")
|
self.logger.info("Start sync worker")
|
||||||
worker = SyncCheckWorker(self.core, self.rgame)
|
worker = SyncCheckWorker(self.core, self.rgame)
|
||||||
worker.signals.error_occurred.connect(self.error_occurred)
|
worker.signals.error_occurred.connect(self.error_occurred)
|
||||||
worker.signals.sync_state_ready.connect(self.sync_ready)
|
worker.signals.sync_state_ready.connect(self.sync_ready)
|
||||||
|
@ -355,7 +350,7 @@ class RareLauncher(RareApp):
|
||||||
self.game_process.finished.disconnect()
|
self.game_process.finished.disconnect()
|
||||||
self.game_process.errorOccurred.disconnect()
|
self.game_process.errorOccurred.disconnect()
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
logger.error(f"Failed to disconnect signals: {e}")
|
self.logger.error(f"Failed to disconnect signals: {e}")
|
||||||
self.logger.info("Stopping server")
|
self.logger.info("Stopping server")
|
||||||
try:
|
try:
|
||||||
self.server.close()
|
self.server.close()
|
||||||
|
@ -369,7 +364,7 @@ class RareLauncher(RareApp):
|
||||||
self.console.on_process_exit(self.rgame.app_name, 0)
|
self.console.on_process_exit(self.rgame.app_name, 0)
|
||||||
|
|
||||||
|
|
||||||
def start_game(args: Namespace):
|
def launch(args: Namespace):
|
||||||
args = InitArgs.from_argparse(args)
|
args = InitArgs.from_argparse(args)
|
||||||
|
|
||||||
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
||||||
|
@ -381,7 +376,7 @@ def start_game(args: Namespace):
|
||||||
# This prevents ghost QLocalSockets, which block the name, which makes it unable to start
|
# This prevents ghost QLocalSockets, which block the name, which makes it unable to start
|
||||||
# No handling for SIGKILL
|
# No handling for SIGKILL
|
||||||
def sighandler(s, frame):
|
def sighandler(s, frame):
|
||||||
logger.info(f"{strsignal(s)} received. Stopping")
|
app.logger.info(f"{strsignal(s)} received. Stopping")
|
||||||
app.stop()
|
app.stop()
|
||||||
app.exit(1)
|
app.exit(1)
|
||||||
signal(SIGINT, sighandler)
|
signal(SIGINT, sighandler)
|
||||||
|
@ -392,4 +387,4 @@ def start_game(args: Namespace):
|
||||||
app.start(args)
|
app.start(args)
|
||||||
# app.exit_app.connect(lambda: app.exit(0))
|
# app.exit_app.connect(lambda: app.exit(0))
|
||||||
|
|
||||||
sys.exit(app.exec_())
|
return app.exec_()
|
103
rare/main.py
Normal file
103
rare/main.py
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
import multiprocessing
|
||||||
|
import os
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
# fix cx_freeze
|
||||||
|
multiprocessing.freeze_support()
|
||||||
|
|
||||||
|
# insert legendary for installed via pip/setup.py submodule to path
|
||||||
|
# if not __name__ == "__main__":
|
||||||
|
# sys.path.insert(0, os.path.join(os.path.dirname(__file__), "legendary"))
|
||||||
|
|
||||||
|
# CLI Options
|
||||||
|
parser = ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"-V", "--version", action="store_true", help="Shows version and exits"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-S",
|
||||||
|
"--silent",
|
||||||
|
action="store_true",
|
||||||
|
help="Launch Rare in background. Open it from System Tray Icon",
|
||||||
|
)
|
||||||
|
parser.add_argument("--debug", action="store_true", help="Launch in debug mode")
|
||||||
|
parser.add_argument(
|
||||||
|
"--offline", action="store_true", help="Launch Rare in offline mode"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--test-start", action="store_true", help="Quit immediately after launch"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--desktop-shortcut",
|
||||||
|
action="store_true",
|
||||||
|
dest="desktop_shortcut",
|
||||||
|
help="Use this, if there is no link on desktop to start Rare",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--startmenu-shortcut",
|
||||||
|
action="store_true",
|
||||||
|
dest="startmenu_shortcut",
|
||||||
|
help="Use this, if there is no link in start menu to launch Rare",
|
||||||
|
)
|
||||||
|
subparsers = parser.add_subparsers(title="Commands", dest="subparser")
|
||||||
|
|
||||||
|
launch_minimal_parser = subparsers.add_parser("start", aliases=["launch"])
|
||||||
|
launch_minimal_parser.add_argument("app_name", help="AppName of the game to launch",
|
||||||
|
metavar="<App Name>", action="store")
|
||||||
|
launch_minimal_parser.add_argument("--dry-run", help="Print arguments and exit", action="store_true")
|
||||||
|
launch_minimal_parser.add_argument("--offline", help="Launch game offline",
|
||||||
|
action="store_true")
|
||||||
|
launch_minimal_parser.add_argument('--wine-bin', dest='wine_bin', action='store', metavar='<wine binary>',
|
||||||
|
default=os.environ.get('LGDRY_WINE_BINARY', None),
|
||||||
|
help='Set WINE binary to use to launch the app')
|
||||||
|
launch_minimal_parser.add_argument('--wine-prefix', dest='wine_pfx', action='store', metavar='<wine pfx path>',
|
||||||
|
default=os.environ.get('LGDRY_WINE_PREFIX', None),
|
||||||
|
help='Set WINE prefix to use')
|
||||||
|
launch_minimal_parser.add_argument("--ask-sync-saves", help="Ask to sync cloud saves",
|
||||||
|
action="store_true")
|
||||||
|
launch_minimal_parser.add_argument("--skip-update-check", help="Do not check for updates",
|
||||||
|
action="store_true")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.desktop_shortcut or args.startmenu_shortcut:
|
||||||
|
from rare.utils.paths import create_desktop_link
|
||||||
|
|
||||||
|
if args.desktop_shortcut:
|
||||||
|
create_desktop_link(app_name="rare_shortcut", link_type="desktop")
|
||||||
|
|
||||||
|
if args.startmenu_shortcut:
|
||||||
|
create_desktop_link(app_name="rare_shortcut", link_type="start_menu")
|
||||||
|
|
||||||
|
print("Link created")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if args.version:
|
||||||
|
from rare import __version__, __codename__
|
||||||
|
print(f"Rare {__version__} Codename: {__codename__}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if args.subparser == "start" or args.subparser == "launch":
|
||||||
|
from rare.launcher import launch
|
||||||
|
return launch(args)
|
||||||
|
|
||||||
|
from rare.utils import singleton
|
||||||
|
|
||||||
|
try:
|
||||||
|
# this object only allows one instance per machine
|
||||||
|
|
||||||
|
me = singleton.SingleInstance()
|
||||||
|
except singleton.SingleInstanceException:
|
||||||
|
print("Rare is already running")
|
||||||
|
from rare.utils.paths import lock_file
|
||||||
|
|
||||||
|
with open(lock_file(), "w") as file:
|
||||||
|
file.write("show")
|
||||||
|
file.close()
|
||||||
|
return -1
|
||||||
|
|
||||||
|
from rare.components import start
|
||||||
|
return start(args)
|
|
@ -550,7 +550,7 @@ class RareGame(RareGameSlim):
|
||||||
|
|
||||||
cmd_line = get_rare_executable()
|
cmd_line = get_rare_executable()
|
||||||
executable, args = cmd_line[0], cmd_line[1:]
|
executable, args = cmd_line[0], cmd_line[1:]
|
||||||
args.extend(["start", self.app_name])
|
args.extend(["launch", self.app_name])
|
||||||
if offline:
|
if offline:
|
||||||
args.append("--offline")
|
args.append("--offline")
|
||||||
if skip_update_check:
|
if skip_update_check:
|
||||||
|
|
|
@ -129,6 +129,7 @@ def desktop_link_path(link_name: str, link_type: str) -> Path:
|
||||||
|
|
||||||
|
|
||||||
def get_rare_executable() -> List[str]:
|
def get_rare_executable() -> List[str]:
|
||||||
|
logger.debug(f"Trying to find executable: {sys.executable}, {sys.argv}")
|
||||||
# lk: detect if nuitka
|
# lk: detect if nuitka
|
||||||
if "__compiled__" in globals():
|
if "__compiled__" in globals():
|
||||||
executable = [sys.executable]
|
executable = [sys.executable]
|
||||||
|
@ -141,7 +142,7 @@ def get_rare_executable() -> List[str]:
|
||||||
if sys.executable == os.path.abspath(sys.argv[0]):
|
if sys.executable == os.path.abspath(sys.argv[0]):
|
||||||
executable = [sys.executable]
|
executable = [sys.executable]
|
||||||
else:
|
else:
|
||||||
executable = [os.path.abspath(sys.argv[0])]
|
executable = [sys.executable, os.path.abspath(sys.argv[0])]
|
||||||
elif platform.system() == "Windows":
|
elif platform.system() == "Windows":
|
||||||
executable = [sys.executable]
|
executable = [sys.executable]
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ class RareAppException(QObject):
|
||||||
exception = pyqtSignal(object, object, object)
|
exception = pyqtSignal(object, object, object)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
self.logger = logging.getLogger(type(self).__name__)
|
|
||||||
super(RareAppException, self).__init__(parent=parent)
|
super(RareAppException, self).__init__(parent=parent)
|
||||||
|
self.logger = logging.getLogger(type(self).__name__)
|
||||||
sys.excepthook = self._excepthook
|
sys.excepthook = self._excepthook
|
||||||
self.exception.connect(self._on_exception)
|
self.exception.connect(self._on_exception)
|
||||||
|
|
||||||
|
@ -37,14 +37,19 @@ class RareAppException(QObject):
|
||||||
if self._handler(exc_type, exc_value, exc_tb):
|
if self._handler(exc_type, exc_value, exc_tb):
|
||||||
return
|
return
|
||||||
self.logger.fatal(message)
|
self.logger.fatal(message)
|
||||||
QMessageBox.warning(None, exc_type.__name__, message)
|
action = QMessageBox.warning(
|
||||||
QApplication.exit(1)
|
None, exc_type.__name__, message,
|
||||||
|
buttons=QMessageBox.Ignore | QMessageBox.Close,
|
||||||
|
defaultButton=QMessageBox.Ignore
|
||||||
|
)
|
||||||
|
if action == QMessageBox.RejectRole:
|
||||||
|
QApplication.exit(1)
|
||||||
|
|
||||||
|
|
||||||
class RareApp(QApplication):
|
class RareApp(QApplication):
|
||||||
def __init__(self, args: Namespace, log_file: str):
|
def __init__(self, args: Namespace, log_file: str):
|
||||||
self.logger = logging.getLogger(type(self).__name__)
|
|
||||||
super(RareApp, self).__init__(sys.argv)
|
super(RareApp, self).__init__(sys.argv)
|
||||||
|
self.logger = logging.getLogger(type(self).__name__)
|
||||||
self._hook = RareAppException(self)
|
self._hook = RareAppException(self)
|
||||||
self.setQuitOnLastWindowClosed(False)
|
self.setQuitOnLastWindowClosed(False)
|
||||||
self.setAttribute(Qt.AA_DontUseNativeDialogs, True)
|
self.setAttribute(Qt.AA_DontUseNativeDialogs, True)
|
||||||
|
|
5
setup.py
5
setup.py
|
@ -45,7 +45,10 @@ setuptools.setup(
|
||||||
],
|
],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
python_requires=">=3.9",
|
python_requires=">=3.9",
|
||||||
entry_points=dict(console_scripts=["rare=rare.__main__:main"]),
|
entry_points={
|
||||||
|
# 'console_scripts': ["rare = rare.main:main"],
|
||||||
|
'gui_scripts': ["rare = rare.main:main"],
|
||||||
|
},
|
||||||
install_requires=requirements,
|
install_requires=requirements,
|
||||||
extras_require=optional_reqs,
|
extras_require=optional_reqs,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue