diff options
| -rwxr-xr-x | gamechestcli/__main__.py | 20 | ||||
| -rw-r--r-- | gamechestcli/gamechest/cliactions/install.py | 7 | ||||
| -rw-r--r-- | gamechestcli/gamechest/cliactions/remove.py | 7 | ||||
| -rw-r--r-- | gamechestcli/gamechest/cliactions/run.py | 3 | ||||
| -rw-r--r-- | gamechestcli/gamechest/gameconfig.py | 99 | ||||
| -rw-r--r-- | gamechestcli/gamechest/gamedb.py | 10 | ||||
| -rw-r--r-- | gamechestcli/gamechest/paths.py | 36 | ||||
| -rw-r--r-- | gamechestcli/gamechest/processor.py | 4 | ||||
| -rw-r--r-- | gamechestcli/gamechest/runners/install.py | 4 | 
9 files changed, 135 insertions, 55 deletions
diff --git a/gamechestcli/__main__.py b/gamechestcli/__main__.py index 1497e40..8fe912d 100755 --- a/gamechestcli/__main__.py +++ b/gamechestcli/__main__.py @@ -4,12 +4,17 @@ Manage games. Install, remove, run them.  Usage: gamechest install <GAME_ID>         gamechest remove <GAME_ID> -       gamechest run --profile_id=<PROFILE_ID> <GAME_ID> +       gamechest run [--profile_id=<PROFILE_ID>] <GAME_ID> +       gamechest set [--profile_id=<PROFILE_ID>] [--remote_basedir=<BASEDIR>] [--gamesaves_path] +  ''' +import sys +  import docopt  from gamechest.cliactions import install, remove, run +from gamechest.gameconfig import config  def main(): @@ -21,7 +26,18 @@ def main():      elif args['remove']:          remove.remove(args['<GAME_ID>'])      elif args['run']: -        run.run(args['<GAME_ID>'], args['--profile_id']) +        profile_id = args['--profile_id'] or config.get_profile_id() +        if not profile_id: +            print('profile_id must be not null', file=sys.stderr) +        run.run(args['<GAME_ID>'], profile_id) +    elif args['set']: +        if args['--profile_id']: +            config.set_profile_id(args['--profile_id']) +        if args['--remote_basedir']: +            config.set_remote_basedir(args['--remote_basedir']) +        if args['--gamesaves_path'] +            config.set_gamesaves_path(args['--gamesaves_path']) +        config.save()  if __name__ == "__main__": diff --git a/gamechestcli/gamechest/cliactions/install.py b/gamechestcli/gamechest/cliactions/install.py index 8584d35..508dd48 100644 --- a/gamechestcli/gamechest/cliactions/install.py +++ b/gamechestcli/gamechest/cliactions/install.py @@ -1,14 +1,15 @@  import functools -from .. import gamedb, paths, processor +from .. import gamedb, processor +from ..gameconfig import config  from ..runners.install import Install  from ..statusdb import StatusDB  def install_game(status_db, game_info): -    remote_basedir = paths.get_remote_basedir() +    remote_basedir = config.get_remote_basedir()      source = f'{remote_basedir}/{game_info["package_name"]}' -    dest = paths.get_games_install_basedir() / game_info['id'] +    dest = config.get_games_install_basedir() / game_info['id']      dest.mkdir(parents=True, exist_ok=True)      title = 'Installing game...'      task = functools.partial(Install, source, dest) diff --git a/gamechestcli/gamechest/cliactions/remove.py b/gamechestcli/gamechest/cliactions/remove.py index d5452d5..6573aff 100644 --- a/gamechestcli/gamechest/cliactions/remove.py +++ b/gamechestcli/gamechest/cliactions/remove.py @@ -5,15 +5,16 @@ import selectors  from rich import print  from rich.progress import Progress as RichProgress -from .. import gamedb, paths, processor +from .. import gamedb, processor +from ..gameconfig import config  from ..runners.remove import Remove  from ..statusdb import StatusDB  def remove_game(status_db, game_info): -    remote_basedir = paths.get_remote_basedir() +    remote_basedir = config.get_remote_basedir()      path = ( -        paths.get_games_install_basedir() +        config.get_games_install_basedir()          / game_info['id']      )      title = 'Removing game...' diff --git a/gamechestcli/gamechest/cliactions/run.py b/gamechestcli/gamechest/cliactions/run.py index b5369aa..d6791d0 100644 --- a/gamechestcli/gamechest/cliactions/run.py +++ b/gamechestcli/gamechest/cliactions/run.py @@ -1,14 +1,13 @@  import subprocess  import sys -from .. import paths  from ..gamedb import GameDB  from ..statusdb import StatusDB  def run_game(game_db, profile_id, game_info):      command = game_db.get_game_command(profile_id, game_info['id']) -    #tools_bin_path = paths.get_games_saves_tools_bin_path() +    #tools_bin_path = config.get_games_saves_tools_bin_path()      #new_env = dict(os.environ)      #new_env['PATH'] = f'{tools_bin_path}:{new_env["PATH"]}'      # path to mod/run scripts are already prepended by get_game_command, no diff --git a/gamechestcli/gamechest/gameconfig.py b/gamechestcli/gamechest/gameconfig.py new file mode 100644 index 0000000..effacd0 --- /dev/null +++ b/gamechestcli/gamechest/gameconfig.py @@ -0,0 +1,99 @@ +import contextlib +from pathlib import Path + +import yaml +from xdg import xdg_data_home + +from . import consts + + +DEFAULT_CONFIG_DICT = { +    'remote_basedir': 'jibril:/storage/games', +    'gamesaves_path': Path('~/syncthing/gamesaves').expanduser(), + +    # profile_id: no default for this, only set by user. +    'profile_id': None, +} + + +def path_relative_to_user(path): +    # first ensure the path is a Path +    path = Path(path) +    with contextlib.suppress(ValueError): +        return '~' / path.relative_to(Path.home()) +    # in case the path is not relative to the user home ValueError is raised, +    # thus we return a fallback value (the path unmodified). +    return path + + +class GameConfig: + +    def __init__(self): +        self.config = {} +        game_config_path = xdg_config_home() / consts.XDG_RESOURCE_NAME +        game_config_path.mkdir(parents=True, exist_ok=True) +        game_config_filepath = game_config_path / 'config.yaml' +        self.game_config_filepath = game_config_filepath +        with contextlib.ExitStack() as stack: +            stack.enter_context(contextlib.suppress(FileNotFoundError)) +            fdin = stack.enter_context(open(game_config_filepath, 'rb')) +            self.config = yaml.safe_load(fdin) +        self.config = { +            **DEFAULT_CONFIG_DICT, +            **self.config, +        } +        # convert game_saves_path to a path if not already the case (in case +        # loaded from yaml file). +        self.config['gamesaves_path'] = ( +            Path(self.config['gamesaves_path']) +            .expanduser() +        ) + +    def get_remote_basedir(self): +        return self.config['remote_basedir'] + +    def get_gamesaves_path(self): +        return self.config['gamesaves_path'] + +    def get_games_saves_tools_bin_path(self): +        return self.get_gamesaves_path() / 'tools' / 'bin' + +    def get_profile_dir(self, profile_id): +        return self.get_gamesaves_path() / 'profiles' / profile_id + +    def get_games_database_path(self): +        return self.get_gamesaves_path() / 'gamedata.yaml' + +    def get_profile_id(self): +        return self.config['profile_id'] + +    def save(self): +        dict_transformed_for_save = { **self.config } +        dict_transformed_for_save['gamesaves_path'] = str( +            path_relative_to_user( +                self.config['gamesaves_path'] +            ) +        ) +        with open(self.game_config_filepath, 'wb') as fdout: +            yaml.safe_dump(dict_transformed_for_save, fdout) + +    def set_profile_id(self, profile_id): +        self.config['profile_id'] = profile_id + +    def set_remote_basedir(self, remote_basedir): +        self.config['remote_basedir'] = remote_basedir + +    def set_gamesaves_path(self, path): +        self.config['gamesaves_path'] = Path(path).expanduser() + +    def get_games_install_basedir(self): +        games_install_path = ( +            xdg_data_home() / consts.XDG_RESOURCE_NAME / 'games' +        ) +        games_install_path.mkdir(parents=True, exist_ok=True) +        return games_install_path + + +# default instance of gameconfig, same instance intended to be shared through +# all modules which needs it. +config = GameConfig() diff --git a/gamechestcli/gamechest/gamedb.py b/gamechestcli/gamechest/gamedb.py index 2000c52..99d3c9a 100644 --- a/gamechestcli/gamechest/gamedb.py +++ b/gamechestcli/gamechest/gamedb.py @@ -1,12 +1,12 @@  import yaml -from . import paths +from .gameconfig import config  class GameDB:      def __init__(self): -        database_path = paths.get_games_database_path() +        database_path = config.get_games_database_path()          with open(database_path, 'rb') as fdin:              self.db = yaml.safe_load(fdin)          self.last_game_info = None @@ -25,12 +25,12 @@ class GameDB:          game_mods = game_info.get('mods', [])          game_mods += ['locked-run-profiledir'] -        game_dir = paths.get_games_install_basedir() / game_info['id'] +        game_dir = config.get_games_install_basedir() / game_info['id']          # note: glob('*/') globs regular files too, thus I filter on is_dir.          game_dir = next(item for item in game_dir.glob('*') if item.is_dir()) -        tools_bin_path = paths.get_games_saves_tools_bin_path() -        profile_dir = paths.get_profile_dir(profile_id) +        tools_bin_path = config.get_games_saves_tools_bin_path() +        profile_dir = config.get_profile_dir(profile_id)          command = [              *[f'{tools_bin_path}/mod-{item}' for item in game_mods], diff --git a/gamechestcli/gamechest/paths.py b/gamechestcli/gamechest/paths.py deleted file mode 100644 index 5e009a6..0000000 --- a/gamechestcli/gamechest/paths.py +++ /dev/null @@ -1,36 +0,0 @@ -import os -from pathlib import Path - -from xdg import xdg_data_home - -from . import consts - - -def get_remote_basedir(): -    # TODO: unhardcode this -    return 'jibril:/storage/games' - - -def get_games_saves_path(): -    # TODO: unhardcode this -    return Path(os.path.expanduser('~/games/.saves')) -    #return Path(os.path.expanduser('~/game-saves')) - - -def get_games_saves_tools_bin_path(): -    return get_games_saves_path() / 'tools' / 'bin' - - -def get_profile_dir(profile_id): -    return get_games_saves_path() / 'profiles' / profile_id - - -def get_games_database_path(): -    return get_games_saves_path() / 'gamedata.yaml' - - -def get_games_install_basedir(): -    games_install_path = xdg_data_home() / consts.XDG_RESOURCE_NAME / 'games' -    games_install_path.mkdir(parents=True, exist_ok=True) -    return games_install_path - diff --git a/gamechestcli/gamechest/processor.py b/gamechestcli/gamechest/processor.py index 89a340c..e31d023 100644 --- a/gamechestcli/gamechest/processor.py +++ b/gamechestcli/gamechest/processor.py @@ -36,6 +36,6 @@ def process_task(title, task):          rich_progress.update(global_id, completed=known_total)          #rich_progress.console.print('installation ended with code:', rc)          if rc != 0: -            # success, update db to say installed -            status_db.set_installed(game_info) +            print('Process failed, aborting') +            raise SystemExit(1)      print('ended with code:', rc) diff --git a/gamechestcli/gamechest/runners/install.py b/gamechestcli/gamechest/runners/install.py index 36b0153..7b2ff7c 100644 --- a/gamechestcli/gamechest/runners/install.py +++ b/gamechestcli/gamechest/runners/install.py @@ -1,5 +1,6 @@  import functools  import os +from pathlib import Path  from .download import Download  from .extract import Extract @@ -10,8 +11,7 @@ from .runnermulti import MultiSequencialRunnerBase  class Install(MultiSequencialRunnerBase):      def __init__(self, source, dest): -        filename = os.path.split(source)[1] -        tmpdest = os.path.join(dest, f'{filename}.rsynctmp') +        tmpdest = Path(dest) / 'archive.rsynctmp'          runners = [              functools.partial(Download, source, tmpdest),              functools.partial(Extract, tmpdest, dest),  | 
