mirror of
https://github.com/derrod/legendary.git
synced 2024-12-22 17:55:27 +00:00
[cli/core/models] Refactor launch parameters and add --json option
Primarily intended to make it easier for third-party applications (mainly Heroic) to handle launch options on their own by simply taking the necessary information from legendary and ignoring user-defined stuff. Also useful for debugging.
This commit is contained in:
parent
ee2432c443
commit
888d62a96d
|
@ -518,7 +518,7 @@ class LegendaryCLI:
|
|||
logger.error('Game is out of date, please update or launch with update check skipping!')
|
||||
exit(1)
|
||||
|
||||
params, cwd, env = self.core.get_launch_parameters(app_name=app_name, offline=args.offline,
|
||||
params = self.core.get_launch_parameters(app_name=app_name, offline=args.offline,
|
||||
extra_args=extra, user=args.user_name_override,
|
||||
wine_bin=args.wine_bin, wine_pfx=args.wine_pfx,
|
||||
language=args.language, wrapper=args.wrapper,
|
||||
|
@ -546,19 +546,37 @@ class LegendaryCLI:
|
|||
if args.wrapper:
|
||||
self.core.lgd.config[app_name]['wrapper'] = args.wrapper
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(vars(params)))
|
||||
return
|
||||
|
||||
full_params = list()
|
||||
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)
|
||||
|
||||
env_overrides = []
|
||||
if params.environment:
|
||||
for env_var, env_value in params.environment.items():
|
||||
if env_var in os.environ:
|
||||
continue
|
||||
env_overrides.append((env_var, env_value))
|
||||
|
||||
if args.dry_run:
|
||||
logger.info(f'Not Launching {app_name} (dry run)')
|
||||
logger.info(f'Launch parameters: {shlex.join(params)}')
|
||||
logger.info(f'Working directory: {cwd}')
|
||||
if env:
|
||||
logger.info('Environment overrides:', env)
|
||||
logger.info(f'Launch parameters: {shlex.join(full_params)}')
|
||||
logger.info(f'Working directory: {params.working_directory}')
|
||||
if env_overrides:
|
||||
logger.info('Environment overrides: {}'.format(', '.join(f'{k}={v}' for k, v in env_overrides)))
|
||||
else:
|
||||
logger.info(f'Launching {app_name}...')
|
||||
logger.debug(f'Launch parameters: {shlex.join(params)}')
|
||||
logger.debug(f'Working directory: {cwd}')
|
||||
if env:
|
||||
logger.debug('Environment overrides:', env)
|
||||
subprocess.Popen(params, cwd=cwd, env=env)
|
||||
logger.debug(f'Launch parameters: {shlex.join(full_params)}')
|
||||
logger.debug(f'Working directory: {params.working_directory}')
|
||||
if env_overrides:
|
||||
logger.debug('Environment overrides: {}'.format(', '.join(f'{k}={v}' for k, v in env_overrides)))
|
||||
subprocess.Popen(full_params, cwd=params.working_directory, env=params.environment)
|
||||
|
||||
def launch_origin(self, args):
|
||||
# login is not required to launch the game, but linking does require it.
|
||||
|
@ -1325,6 +1343,8 @@ def main():
|
|||
help='Override executable to launch (relative path)')
|
||||
launch_parser.add_argument('--origin', dest='origin', action='store_true',
|
||||
help='Launch Origin to activate or run the game.')
|
||||
launch_parser.add_argument('--json', dest='json', action='store_true',
|
||||
help='Print launch information as JSON and exit')
|
||||
|
||||
if os.name != 'nt':
|
||||
launch_parser.add_argument('--wine', dest='wine_bin', action='store', metavar='<wine binary>',
|
||||
|
|
|
@ -438,10 +438,48 @@ class LegendaryCore:
|
|||
wine_bin: str = None, wine_pfx: str = None,
|
||||
language: str = None, wrapper: str = None,
|
||||
disable_wine: bool = False,
|
||||
executable_override: str = None) -> (list, str, dict):
|
||||
executable_override: str = None) -> LaunchParameters:
|
||||
install = self.lgd.get_installed_game(app_name)
|
||||
game = self.lgd.get_game_meta(app_name)
|
||||
|
||||
if executable_override or (executable_override := self.lgd.config.get(app_name, 'override_exe', fallback=None)):
|
||||
game_exe = executable_override.replace('\\', '/')
|
||||
exe_path = os.path.join(install.install_path, game_exe)
|
||||
if not os.path.exists(exe_path):
|
||||
raise ValueError(f'Executable path is invalid: {exe_path}')
|
||||
else:
|
||||
game_exe = install.executable.replace('\\', '/').lstrip('/')
|
||||
exe_path = os.path.join(install.install_path, game_exe)
|
||||
|
||||
working_dir = os.path.split(exe_path)[0]
|
||||
|
||||
params = LaunchParameters(game_executable=game_exe, game_directory=install.install_path,
|
||||
working_directory=working_dir,
|
||||
environment=self.get_app_environment(app_name, wine_pfx=wine_pfx))
|
||||
|
||||
if wrapper or (wrapper := self.lgd.config.get(app_name, 'wrapper',
|
||||
fallback=self.lgd.config.get('default', 'wrapper',
|
||||
fallback=None))):
|
||||
params.launch_command.extend(shlex.split(wrapper))
|
||||
|
||||
if os.name != 'nt' and not disable_wine:
|
||||
if not wine_bin:
|
||||
# check if there's a default override
|
||||
wine_bin = self.lgd.config.get('default', 'wine_executable', fallback='wine')
|
||||
# check if there's a game specific override
|
||||
wine_bin = self.lgd.config.get(app_name, 'wine_executable', fallback=wine_bin)
|
||||
|
||||
if not self.lgd.config.getboolean(app_name, 'no_wine',
|
||||
fallback=self.lgd.config.get('default', 'no_wine', fallback=False)):
|
||||
params.launch_command.append(wine_bin)
|
||||
|
||||
if install.launch_parameters:
|
||||
try:
|
||||
params.game_parameters.extend(shlex.split(install.launch_parameters, posix=False))
|
||||
except ValueError as e:
|
||||
self.log.warning(f'Parsing predefined launch parameters failed with: {e!r}, '
|
||||
f'input: {install.launch_parameters}')
|
||||
|
||||
game_token = ''
|
||||
if not offline:
|
||||
self.log.info('Getting authentication token...')
|
||||
|
@ -454,45 +492,7 @@ class LegendaryCore:
|
|||
if user:
|
||||
user_name = user
|
||||
|
||||
if executable_override or (executable_override := self.lgd.config.get(app_name, 'override_exe', fallback=None)):
|
||||
game_exe = os.path.join(install.install_path,
|
||||
executable_override.replace('\\', '/'))
|
||||
if not os.path.exists(game_exe):
|
||||
raise ValueError(f'Executable path is invalid: {game_exe}')
|
||||
else:
|
||||
game_exe = os.path.join(install.install_path,
|
||||
install.executable.replace('\\', '/').lstrip('/'))
|
||||
|
||||
working_dir = os.path.split(game_exe)[0]
|
||||
|
||||
params = []
|
||||
|
||||
if wrapper or (wrapper := self.lgd.config.get(app_name, 'wrapper',
|
||||
fallback=self.lgd.config.get('default', 'wrapper',
|
||||
fallback=None))):
|
||||
params.extend(shlex.split(wrapper))
|
||||
|
||||
if os.name != 'nt' and not disable_wine:
|
||||
if not wine_bin:
|
||||
# check if there's a default override
|
||||
wine_bin = self.lgd.config.get('default', 'wine_executable', fallback='wine')
|
||||
# check if there's a game specific override
|
||||
wine_bin = self.lgd.config.get(app_name, 'wine_executable', fallback=wine_bin)
|
||||
|
||||
if not self.lgd.config.getboolean(app_name, 'no_wine',
|
||||
fallback=self.lgd.config.get('default', 'no_wine', fallback=False)):
|
||||
params.append(wine_bin)
|
||||
|
||||
params.append(game_exe)
|
||||
|
||||
if install.launch_parameters:
|
||||
try:
|
||||
params.extend(shlex.split(install.launch_parameters, posix=False))
|
||||
except ValueError as e:
|
||||
self.log.warning(f'Parsing predefined launch parameters failed with: {e!r}, '
|
||||
f'input: {install.launch_parameters}')
|
||||
|
||||
params.extend([
|
||||
params.egl_parameters.extend([
|
||||
'-AUTH_LOGIN=unused',
|
||||
f'-AUTH_PASSWORD={game_token}',
|
||||
'-AUTH_TYPE=exchangecode',
|
||||
|
@ -507,13 +507,13 @@ class LegendaryCore:
|
|||
f'{game.asset_info.namespace}{game.asset_info.catalog_item_id}.ovt')
|
||||
with open(ovt_path, 'wb') as f:
|
||||
f.write(ovt)
|
||||
params.append(f'-epicovt={ovt_path}')
|
||||
params.egl_parameters.append(f'-epicovt={ovt_path}')
|
||||
|
||||
language_code = self.lgd.config.get(app_name, 'language', fallback=language)
|
||||
if not language_code: # fall back to system or config language
|
||||
language_code = self.language_code
|
||||
|
||||
params.extend([
|
||||
params.egl_parameters.extend([
|
||||
'-EpicPortal',
|
||||
f'-epicusername={user_name}',
|
||||
f'-epicuserid={account_id}',
|
||||
|
@ -521,13 +521,12 @@ class LegendaryCore:
|
|||
])
|
||||
|
||||
if extra_args:
|
||||
params.extend(extra_args)
|
||||
params.user_parameters.extend(extra_args)
|
||||
|
||||
if config_args := self.lgd.config.get(app_name, 'start_params', fallback=None):
|
||||
params.extend(shlex.split(config_args.strip()))
|
||||
params.user_parameters.extend(shlex.split(config_args.strip()))
|
||||
|
||||
env = self.get_app_environment(app_name, wine_pfx=wine_pfx)
|
||||
return params, working_dir, env
|
||||
return params
|
||||
|
||||
def get_origin_uri(self, app_name: str, offline: bool = False) -> str:
|
||||
if offline:
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# coding: utf-8
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class GameAsset:
|
||||
def __init__(self):
|
||||
self.app_name = ''
|
||||
|
@ -144,3 +144,20 @@ class VerifyResult(Enum):
|
|||
HASH_MISMATCH = 1
|
||||
FILE_MISSING = 2
|
||||
OTHER_ERROR = 3
|
||||
|
||||
|
||||
@dataclass
|
||||
class LaunchParameters:
|
||||
# game-supplied parameters
|
||||
game_parameters: list = field(default_factory=list)
|
||||
game_executable: str = ''
|
||||
game_directory: str = ''
|
||||
# EGL parameters (auth, ovt, etc.)
|
||||
egl_parameters: list = field(default_factory=list)
|
||||
# command line before executable (WINE, gamemode, etc.)
|
||||
launch_command: list = field(default_factory=list)
|
||||
# working directory for launched process
|
||||
working_directory: str = ''
|
||||
# user and environment supplied options
|
||||
user_parameters: list = field(default_factory=list)
|
||||
environment: dict = field(default_factory=dict)
|
||||
|
|
Loading…
Reference in a new issue