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!')
|
logger.error('Game is out of date, please update or launch with update check skipping!')
|
||||||
exit(1)
|
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,
|
extra_args=extra, user=args.user_name_override,
|
||||||
wine_bin=args.wine_bin, wine_pfx=args.wine_pfx,
|
wine_bin=args.wine_bin, wine_pfx=args.wine_pfx,
|
||||||
language=args.language, wrapper=args.wrapper,
|
language=args.language, wrapper=args.wrapper,
|
||||||
|
@ -546,19 +546,37 @@ class LegendaryCLI:
|
||||||
if args.wrapper:
|
if args.wrapper:
|
||||||
self.core.lgd.config[app_name]['wrapper'] = 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:
|
if args.dry_run:
|
||||||
logger.info(f'Not Launching {app_name} (dry run)')
|
logger.info(f'Not Launching {app_name} (dry run)')
|
||||||
logger.info(f'Launch parameters: {shlex.join(params)}')
|
logger.info(f'Launch parameters: {shlex.join(full_params)}')
|
||||||
logger.info(f'Working directory: {cwd}')
|
logger.info(f'Working directory: {params.working_directory}')
|
||||||
if env:
|
if env_overrides:
|
||||||
logger.info('Environment overrides:', env)
|
logger.info('Environment overrides: {}'.format(', '.join(f'{k}={v}' for k, v in env_overrides)))
|
||||||
else:
|
else:
|
||||||
logger.info(f'Launching {app_name}...')
|
logger.info(f'Launching {app_name}...')
|
||||||
logger.debug(f'Launch parameters: {shlex.join(params)}')
|
logger.debug(f'Launch parameters: {shlex.join(full_params)}')
|
||||||
logger.debug(f'Working directory: {cwd}')
|
logger.debug(f'Working directory: {params.working_directory}')
|
||||||
if env:
|
if env_overrides:
|
||||||
logger.debug('Environment overrides:', env)
|
logger.debug('Environment overrides: {}'.format(', '.join(f'{k}={v}' for k, v in env_overrides)))
|
||||||
subprocess.Popen(params, cwd=cwd, env=env)
|
subprocess.Popen(full_params, cwd=params.working_directory, env=params.environment)
|
||||||
|
|
||||||
def launch_origin(self, args):
|
def launch_origin(self, args):
|
||||||
# login is not required to launch the game, but linking does require it.
|
# 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)')
|
help='Override executable to launch (relative path)')
|
||||||
launch_parser.add_argument('--origin', dest='origin', action='store_true',
|
launch_parser.add_argument('--origin', dest='origin', action='store_true',
|
||||||
help='Launch Origin to activate or run the game.')
|
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':
|
if os.name != 'nt':
|
||||||
launch_parser.add_argument('--wine', dest='wine_bin', action='store', metavar='<wine binary>',
|
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,
|
wine_bin: str = None, wine_pfx: str = None,
|
||||||
language: str = None, wrapper: str = None,
|
language: str = None, wrapper: str = None,
|
||||||
disable_wine: bool = False,
|
disable_wine: bool = False,
|
||||||
executable_override: str = None) -> (list, str, dict):
|
executable_override: str = None) -> LaunchParameters:
|
||||||
install = self.lgd.get_installed_game(app_name)
|
install = self.lgd.get_installed_game(app_name)
|
||||||
game = self.lgd.get_game_meta(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 = ''
|
game_token = ''
|
||||||
if not offline:
|
if not offline:
|
||||||
self.log.info('Getting authentication token...')
|
self.log.info('Getting authentication token...')
|
||||||
|
@ -454,45 +492,7 @@ class LegendaryCore:
|
||||||
if user:
|
if user:
|
||||||
user_name = user
|
user_name = user
|
||||||
|
|
||||||
if executable_override or (executable_override := self.lgd.config.get(app_name, 'override_exe', fallback=None)):
|
params.egl_parameters.extend([
|
||||||
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([
|
|
||||||
'-AUTH_LOGIN=unused',
|
'-AUTH_LOGIN=unused',
|
||||||
f'-AUTH_PASSWORD={game_token}',
|
f'-AUTH_PASSWORD={game_token}',
|
||||||
'-AUTH_TYPE=exchangecode',
|
'-AUTH_TYPE=exchangecode',
|
||||||
|
@ -507,13 +507,13 @@ class LegendaryCore:
|
||||||
f'{game.asset_info.namespace}{game.asset_info.catalog_item_id}.ovt')
|
f'{game.asset_info.namespace}{game.asset_info.catalog_item_id}.ovt')
|
||||||
with open(ovt_path, 'wb') as f:
|
with open(ovt_path, 'wb') as f:
|
||||||
f.write(ovt)
|
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)
|
language_code = self.lgd.config.get(app_name, 'language', fallback=language)
|
||||||
if not language_code: # fall back to system or config language
|
if not language_code: # fall back to system or config language
|
||||||
language_code = self.language_code
|
language_code = self.language_code
|
||||||
|
|
||||||
params.extend([
|
params.egl_parameters.extend([
|
||||||
'-EpicPortal',
|
'-EpicPortal',
|
||||||
f'-epicusername={user_name}',
|
f'-epicusername={user_name}',
|
||||||
f'-epicuserid={account_id}',
|
f'-epicuserid={account_id}',
|
||||||
|
@ -521,13 +521,12 @@ class LegendaryCore:
|
||||||
])
|
])
|
||||||
|
|
||||||
if extra_args:
|
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):
|
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
|
||||||
return params, working_dir, env
|
|
||||||
|
|
||||||
def get_origin_uri(self, app_name: str, offline: bool = False) -> str:
|
def get_origin_uri(self, app_name: str, offline: bool = False) -> str:
|
||||||
if offline:
|
if offline:
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
class GameAsset:
|
class GameAsset:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.app_name = ''
|
self.app_name = ''
|
||||||
|
@ -144,3 +144,20 @@ class VerifyResult(Enum):
|
||||||
HASH_MISMATCH = 1
|
HASH_MISMATCH = 1
|
||||||
FILE_MISSING = 2
|
FILE_MISSING = 2
|
||||||
OTHER_ERROR = 3
|
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