Example #1
0
 def test_game_with_same_slug_is_updated(self):
     pga.add_game(name="some game", runner="linux")
     game = pga.get_game_by_field("some-game", "slug")
     self.assertFalse(game['directory'])
     pga.add_or_update(name="some game", runner='linux', directory="/foo")
     game = pga.get_game_by_field("some-game", "slug")
     self.assertEqual(game['directory'], '/foo')
Example #2
0
    def _get_installed_dependency(self, dependency):
        """Return whether a dependency is installed"""
        game = pga.get_game_by_field(dependency, field='installer_slug')

        if not game:
            game = pga.get_game_by_field(dependency, 'slug')
        if bool(game) and bool(game['directory']):
            return game
Example #3
0
    def _get_installed_dependency(dependency):
        """Return whether a dependency is installed"""
        game = pga.get_game_by_field(dependency, field="installer_slug")

        if not game:
            game = pga.get_game_by_field(dependency, "slug")
        if bool(game) and bool(game["directory"]):
            return game
Example #4
0
    def _check_dependency(self):
        # XXX Maybe handle this with Game instead of hitting directly the PGA?
        game = pga.get_game_by_field(self.requires, field='installer_slug')
        # Legacy support of installers using game slug as requirement
        if not game:
            game = pga.get_game_by_field(self.requires, 'slug')

        if not game or not game['directory']:
            raise ScriptingError(
                "You need to install {} before".format(self.requires)
            )
        self.target_path = game['directory']
Example #5
0
    def __init__(self, script, parent):
        self.error = None
        self.errors = []
        self.files = []
        self.target_path = None
        self.parent = parent
        self.reversion_data = {}
        self.game_name = None
        self.game_slug = None
        self.game_files = {}
        self.game_disc = None
        self.cancelled = False
        self.abort_current_task = None
        self.user_inputs = []
        self.steam_data = {}
        self.script = script
        if not self.script:
            return
        if not self.is_valid():
            raise ScriptingError("Invalid script", (self.script, self.errors))
        self.game_name = self.script['name']
        self.game_slug = self.script['game_slug']
        self.requires = self.script.get('requires')
        if self.requires:
            self._check_dependency()
        else:
            self.target_path = self.default_target

        # If the game is in the library and uninstalled, the first installation
        # updates it
        existing_game = pga.get_game_by_field(self.game_slug, 'slug')
        if existing_game and not existing_game['installed']:
            self.game_id = existing_game['id']
        else:
            self.game_id = None
Example #6
0
File: game.py Project: Freso/lutris
    def __init__(self, id=None):
        self.id = id
        self.runner = None
        self.game_thread = None
        self.heartbeat = None
        self.config = None
        self.killswitch = None
        self.state = self.STATE_IDLE
        self.game_log = ''
        self.exit_main_loop = False

        game_data = pga.get_game_by_field(id, 'id')
        self.slug = game_data.get('slug') or ''
        self.runner_name = game_data.get('runner') or ''
        self.directory = game_data.get('directory') or ''
        self.name = game_data.get('name') or ''
        self.is_installed = bool(game_data.get('installed')) or False
        self.year = game_data.get('year') or ''
        self.game_config_id = game_data.get('configpath') or ''
        self.steamid = game_data.get('steamid') or ''
        self.has_custom_banner = bool(game_data.get('has_custom_banner')) or False
        self.has_custom_icon = bool(game_data.get('has_custom_icon')) or False

        self.load_config()
        self.resolution_changed = False
        self.original_outputs = None
Example #7
0
 def on_steam_game_changed(self, operation, path):
     appmanifest = steam.AppManifest(path)
     runner_name = appmanifest.get_runner_name()
     games = pga.get_game_by_field(appmanifest.steamid, field='steamid', all=True)
     if operation == Gio.FileMonitorEvent.DELETED:
         for game in games:
             if game['runner'] == runner_name:
                 steam.mark_as_uninstalled(game)
                 self.view.set_uninstalled(Game(game['id']))
                 break
     elif operation in (Gio.FileMonitorEvent.CHANGED, Gio.FileMonitorEvent.CREATED):
         if not appmanifest.is_installed():
             return
         if runner_name == 'winesteam':
             return
         game_info = None
         for game in games:
             if game['installed'] == 0:
                 game_info = game
             else:
                 # Game is already installed, don't do anything
                 return
         if not game_info:
             game_info = {
                 'name': appmanifest.name,
                 'slug': appmanifest.slug,
             }
         game_id = steam.mark_as_installed(appmanifest.steamid,
                                           runner_name,
                                           game_info)
         game_ids = [game['id'] for game in self.game_list]
         if game_id not in game_ids:
             self.add_game_to_view(game_id)
         else:
             self.view.set_installed(Game(game_id))
Example #8
0
 def on_steam_game_changed(self, operation, path):
     appmanifest = steam.AppManifest(path)
     runner_name = appmanifest.get_runner_name()
     games = pga.get_game_by_field(appmanifest.steamid, field='steamid', all=True)
     if operation == 'DELETE':
         for game in games:
             if game['runner'] == runner_name:
                 steam.mark_as_uninstalled(game)
                 self.view.set_uninstalled(Game(game['id']))
                 break
     elif operation in ('MODIFY', 'CREATE'):
         if not appmanifest.is_installed():
             return
         if runner_name == 'windows':
             return
         game_info = None
         for game in games:
             if game['installed'] == 0:
                 game_info = game
         if not game_info:
             game_info = {
                 'name': appmanifest.name,
                 'slug': appmanifest.slug,
             }
         game_id = steam.mark_as_installed(appmanifest.steamid,
                                           runner_name,
                                           game_info)
         game_ids = [game['id'] for game in self.game_list]
         if game_id not in game_ids:
             self.add_game_to_view(game_id)
         else:
             self.view.set_installed(Game(game_id))
Example #9
0
    def run_no_installer_dialog(self):
        """Open dialog for 'no script available' situation."""
        dlg = NoInstallerDialog(self)
        if dlg.result == dlg.MANUAL_CONF:
            game_data = pga.get_game_by_field(self.game_slug, 'slug')

            if game_data and 'slug' in game_data:
                # Game data already exist locally.
                game = Game(game_data['id'])
            else:
                # Try to load game data from remote.
                games = api.get_games([self.game_slug])

                if games and len(games) >= 1:
                    remote_game = games[0]
                    game_data = {
                        'name': remote_game['name'],
                        'slug': remote_game['slug'],
                        'year': remote_game['year'],
                        'updated': remote_game['updated'],
                        'steamid': remote_game['steamid']
                    }
                    game = Game(pga.add_game(**game_data))
                else:
                    game = None

            AddGameDialog(
                self.parent,
                game=game,
                callback=lambda: self.notify_install_success(game_data['id'])
            )
        elif dlg.result == dlg.NEW_INSTALLER:
            webbrowser.open(settings.GAME_URL % self.game_slug)
Example #10
0
    def run_no_installer_dialog(self):
        """Open dialog for 'no script available' situation."""
        dlg = NoInstallerDialog(self)
        if dlg.result == dlg.MANUAL_CONF:
            game_data = pga.get_game_by_field(self.game_slug, "slug")

            if game_data and "slug" in game_data:
                # Game data already exist locally.
                game = Game(game_data["id"])
            else:
                # Try to load game data from remote.
                games = api.get_api_games([self.game_slug])

                if games and len(games) >= 1:
                    remote_game = games[0]
                    game_data = {
                        "name": remote_game["name"],
                        "slug": remote_game["slug"],
                        "year": remote_game["year"],
                        "updated": remote_game["updated"],
                        "steamid": remote_game["steamid"],
                    }
                    game = Game(pga.add_game(**game_data))
                else:
                    game = None
            AddGameDialog(self.parent, game=game)
        elif dlg.result == dlg.NEW_INSTALLER:
            webbrowser.open(settings.GAME_URL % self.game_slug)
Example #11
0
 def on_image_downloaded(self, game_slugs):
     for game_slug in game_slugs:
         games = pga.get_game_by_field(game_slug, field='slug', all=True)
         for game in games:
             game = Game(game['id'])
             is_installed = game.is_installed
             self.view.update_image(game.id, is_installed)
Example #12
0
    def __init__(self, id=None):
        self.id = id
        self.runner = None
        self.game_thread = None
        self.heartbeat = None
        self.config = None
        self.killswitch = None
        self.state = self.STATE_IDLE
        self.exit_main_loop = False

        game_data = pga.get_game_by_field(id, 'id')
        self.slug = game_data.get('slug') or ''
        self.runner_name = game_data.get('runner') or ''
        self.directory = game_data.get('directory') or ''
        self.name = game_data.get('name') or ''

        self.is_installed = bool(game_data.get('installed')) or False
        self.platform = game_data.get('platform') or ''
        self.year = game_data.get('year') or ''
        self.lastplayed = game_data.get('lastplayed') or 0
        self.game_config_id = game_data.get('configpath') or ''
        self.steamid = game_data.get('steamid') or ''
        self.has_custom_banner = bool(game_data.get('has_custom_banner')) or False
        self.has_custom_icon = bool(game_data.get('has_custom_icon')) or False

        self.load_config()
        self.resolution_changed = False
        self.original_outputs = None
        self.log_buffer = Gtk.TextBuffer()
        self.log_buffer.create_tag("warning", foreground="red")
Example #13
0
    def update_existing_games(self, added, updated, first_run=False):
        for game_id in updated.difference(added):
            self.view.update_row(pga.get_game_by_field(game_id, 'id'))

        if first_run:
            icons_sync = AsyncCall(self.sync_icons, callback=None)
            self.threads_stoppers.append(icons_sync.stop_request.set)
            self.set_status("")
Example #14
0
 def __init__(self, config=None):
     """Initialize runner."""
     self.arch = system.LINUX_SYSTEM.arch
     self.config = config
     self.game_data = {}
     if config:
         self.game_data = pga.get_game_by_field(
             self.config.game_config_id, "configpath"
         )
Example #15
0
 def add_game_by_id(self, game_id):
     if not game_id:
         return
     game = pga.get_game_by_field(game_id, 'id')
     if not game or 'slug' not in game:
         raise ValueError('Can\'t find game {} ({})'.format(
             game_id, game
         ))
     self.add_game(game)
Example #16
0
 def on_install_clicked(self, _widget=None, game_ref=None):
     """Install a game"""
     if not game_ref:
         game_id = self._get_current_game_id()
         game = pga.get_game_by_field(game_id, 'id')
         game_ref = game.get('slug')
         logger.debug("Installing game %s (%s)" % (game_ref, game_id))
     if not game_ref:
         return
     InstallerDialog(game_ref, self)
Example #17
0
 def update_gui(result, error):
     if result:
         added_ids, updated_ids = result
         added_games = pga.get_game_by_field(added_ids, 'id', all=True)
         self.game_list += added_games
         self.view.populate_games(added_games)
         self.switch_splash_screen()
         GLib.idle_add(self.update_existing_games, added_ids, updated_ids, True)
     else:
         logger.error("No results returned when syncing the library")
Example #18
0
 def __init__(self, config=None):
     """Initialize runner."""
     self.arch = get_arch()
     self.logger = logger
     self.config = config
     self.game_data = {}
     if config:
         self.game_data = pga.get_game_by_field(
             self.config.game_config_id, 'configpath'
         )
Example #19
0
 def add_game_by_id(self, game_id):
     """Add a game into the store."""
     if not game_id:
         return
     game = pga.get_game_by_field(game_id, 'id')
     if not game or 'slug' not in game:
         raise ValueError('Can\'t find game {} ({})'.format(
             game_id, game
         ))
     self.add_game(game)
Example #20
0
 def on_install_clicked(self, *args, game_ref=None):
     """Install a game"""
     if not game_ref:
         game_id = self._get_current_game_id()
         game = pga.get_game_by_field(game_id, 'id')
         game_ref = game.get('slug')
         logger.debug("Installing game %s (%s)" % (game_ref, game_id))
     if not game_ref:
         return
     InstallerDialog(game_ref, self)
Example #21
0
 def __init__(self, config=None):
     """Initialize runner."""
     self.arch = get_arch()
     self.logger = logger
     self.config = config
     self.game_data = {}
     if config:
         self.game_data = pga.get_game_by_field(
             self.config.game_config_id, 'configpath'
         )
Example #22
0
 def __init__(self, config=None):
     """Initialize runner."""
     self.arch = system.LINUX_SYSTEM.arch
     self.logger = logger
     self.config = config
     self.game_data = {}
     if config:
         self.game_data = pga.get_game_by_field(
             self.config.game_config_id, "configpath"
         )
Example #23
0
    def sync_game_details(remote_library):
        """Update local game details,

        :return: The slugs of the updated games.
        :rtype: set
        """
        if not remote_library:
            return set()
        updated = set()

        # Get remote games (TODO: use this when switched API to DRF)
        # remote_games = get_games(sorted(local_slugs))
        # if not remote_games:
        #     return set()

        for game in remote_library:
            slug = game['slug']
            sync = False
            sync_icons = True
            local_game = pga.get_game_by_field(slug, 'slug')
            if not local_game:
                continue

            # Sync updated
            if game['updated'] > local_game['updated']:
                sync = True
            # Sync new DB fields
            else:
                for key, value in local_game.iteritems():
                    if value or key not in game:
                        continue
                    if game[key]:
                        sync = True
                        sync_icons = False
            if not sync:
                continue

            logger.debug("Syncing details for %s" % slug)
            game_id = pga.add_or_update(
                name=local_game['name'],
                runner=local_game['runner'],
                slug=slug,
                year=game['year'],
                updated=game['updated'],
                steamid=game['steamid']
            )

            # Sync icons (TODO: Only update if icon actually updated)
            if sync_icons:
                resources.download_icon(slug, 'banner', overwrite=True)
                resources.download_icon(slug, 'icon', overwrite=True)
                updated.add(game_id)

        logger.debug("%d games updated", len(updated))
        return updated
Example #24
0
def sync_game_details(remote_library):
    """Update local game details,

    :return: A set of ids of the updated games.
    """
    if not remote_library:
        return set()
    updated = set()

    for remote_game in remote_library:
        slug = remote_game['slug']
        sync_required = False
        local_game = pga.get_game_by_field(slug, 'slug')
        if not local_game:
            continue

        if local_game[
                'updated'] and remote_game['updated'] > local_game['updated']:
            # The remote game's info is more recent than the local game
            sync_required = True
        else:
            for key in remote_game.keys():
                if key in local_game.keys(
                ) and remote_game[key] and not local_game[key]:
                    # Remote game has data that is missing from the local game.
                    sync_required = True
                    break

        if not sync_required:
            continue

        logger.debug("Syncing details for %s" % slug)
        game_id = pga.add_or_update(name=local_game['name'],
                                    runner=local_game['runner'],
                                    slug=slug,
                                    year=remote_game['year'],
                                    updated=remote_game['updated'],
                                    steamid=remote_game['steamid'])
        updated.add(game_id)

        if not local_game.get(
                'has_custom_banner') and remote_game['banner_url']:
            path = resources.get_icon_path(slug, resources.BANNER)
            resources.download_media(remote_game['banner_url'],
                                     path,
                                     overwrite=True)
        if not local_game.get('has_custom_icon') and remote_game['icon_url']:
            path = resources.get_icon_path(slug, resources.ICON)
            resources.download_media(remote_game['icon_url'],
                                     path,
                                     overwrite=True)

    if updated:
        logger.debug("%d games updated", len(updated))
    return updated
Example #25
0
 def update_gui(result, error):
     if result:
         added_ids, updated_ids = result
         added_games = pga.get_game_by_field(added_ids, 'id', all=True)
         self.game_list += added_games
         self.view.populate_games(added_games)
         self.switch_splash_screen()
         GLib.idle_add(self.update_existing_games, added_ids,
                       updated_ids, True)
     else:
         logger.error("No results returned when syncing the library")
Example #26
0
 def on_install_clicked(self, _widget=None, game_ref=None):
     """Install a game"""
     if not game_ref:
         game_id = self._get_current_game_id()
         game = pga.get_game_by_field(game_id, "id")
         game_ref = game.get("slug")
         logger.debug("Installing game %s (%s)" % (game_ref, game_id))
     if not game_ref:
         return
     display.set_cursor("wait", self.window.get_window())
     InstallerDialog(game_ref, self)
Example #27
0
    def update_existing_games(self, added, updated, first_run=False):
        """???"""
        for game_id in updated.difference(added):
            game = pga.get_game_by_field(game_id, "id")
            self.view.update_row(game["id"], game["year"], game["playtime"])

        if first_run:
            self.update_games(added)
            game_slugs = [game["slug"] for game in self.game_list]
            AsyncCall(resources.get_missing_media, self.on_media_returned,
                      game_slugs)
Example #28
0
    def on_game_installed(self, view, game_id):
        if type(game_id) != int:
            raise ValueError("game_id must be an int")
        if not self.view.get_row_by_id(game_id):
            logger.debug("Adding new installed game to view (%d)" % game_id)
            self.add_game_to_view(game_id, async=False)

        view.set_installed(Game(game_id))
        self.sidebar_treeview.update()
        game_data = pga.get_game_by_field(game_id, field='id')
        GLib.idle_add(resources.fetch_icons,
                      [game_data], self.on_image_downloaded)
 def run_no_installer_dialog(self):
     """Open dialog for 'no script available' situation."""
     dlg = NoInstallerDialog(self)
     if dlg.result == dlg.MANUAL_CONF:
         game_data = pga.get_game_by_field(self.game_slug, 'slug')
         game = Game(game_data['id'])
         AddGameDialog(
             self.parent,
             game=game,
             callback=lambda: self.notify_install_success(game_data['id']))
     elif dlg.result == dlg.NEW_INSTALLER:
         webbrowser.open(settings.GAME_URL % self.game_slug)
Example #30
0
 def __init__(self, config=None):
     """Initialize runner."""
     self.arch = system.LINUX_SYSTEM.arch
     self.config = config
     self.game_data = {}
     if config:
         self.game_data = pga.get_game_by_field(self.config.game_config_id,
                                                "configpath")
     self.discord_presence = Presence()
     if self.discord_presence.available():
         self.common_game_options = self.common_game_options + self.discord_presence_options
     self.inject_common_game_options()
Example #31
0
    def set_status(self, text):
        """Sets the statusbar text"""
        # update row at game exit
        # XXX This is NOT a proper way to do it!!!!!!
        # FIXME This is ugly and will cause issues!@@!
        if text == "Game has quit" and self.gui_needs_update:
            self.view.update_row(pga.get_game_by_field(self.running_game.id, 'id'))

        for child_widget in self.status_box.get_children():
            child_widget.destroy()
        label = Gtk.Label(text)
        label.show()
        self.status_box.add(label)
Example #32
0
 def on_install_clicked(self, *args, game_slug=None, installer_file=None, revision=None):
     """Install a game"""
     if not game_slug:
         game_id = self._get_current_game_id()
         game = pga.get_game_by_field(game_id, 'id')
         game_slug = game.get('slug')
         logger.debug("Installing game %s (%s)" % (game_slug, game_id))
     if not game_slug:
         return
     InstallerDialog(game_slug=game_slug,
                     installer_file=installer_file,
                     revision=revision,
                     parent=self)
Example #33
0
 def run_no_installer_dialog(self):
     """Open dialog for 'no script available' situation."""
     dlg = NoInstallerDialog(self)
     if dlg.result == dlg.MANUAL_CONF:
         game_data = pga.get_game_by_field(self.game_slug, 'slug')
         game = Game(game_data['id'])
         AddGameDialog(
             self.parent,
             game=game,
             callback=lambda: self.notify_install_success(game_data['id'])
         )
     elif dlg.result == dlg.NEW_INSTALLER:
         webbrowser.open(settings.GAME_URL % self.game_slug)
Example #34
0
    def __init__(self, installer, parent):
        self.error = None
        self.errors = []
        self.target_path = None
        self.parent = parent
        self.reversion_data = {}
        self.game_name = None
        self.game_slug = None
        self.game_files = {}
        self.game_disc = None
        self.cancelled = False
        self.abort_current_task = None
        self.user_inputs = []
        self.steam_data = {}
        self.script = installer.get('script')
        if not self.script:
            raise ScriptingError(
                "This installer doesn't have a 'script' section")

        self.runners_to_install = []
        self.prev_states = []  # Previous states for the Steam installer

        self.version = installer['version']
        self.slug = installer['slug']
        self.year = installer.get('year')
        self.name = installer['name']
        self.runner = installer['runner']
        self.game_name = installer['name']
        self.game_slug = installer['game_slug']
        self.steamid = installer.get('steamid')

        if not self.is_valid():
            raise ScriptingError(
                "Invalid script: \n{}".format("\n".join(self.errors)),
                self.script)

        self.files = self.script.get('files', [])
        self.requires = self.script.get('requires')
        self.extends = self.script.get('extends')

        self._check_dependency()
        if self.creates_game_folder:
            self.target_path = self.get_default_target()

        # If the game is in the library and uninstalled, the first installation
        # updates it
        existing_game = pga.get_game_by_field(self.game_slug, 'slug')
        if existing_game and not existing_game['installed']:
            self.game_id = existing_game['id']
        else:
            self.game_id = None
Example #35
0
File: sync.py Project: Freso/lutris
def sync_game_details(remote_library):
    """Update local game details,

    :return: A set of ids of the updated games.
    """
    if not remote_library:
        return set()
    updated = set()

    for remote_game in remote_library:
        slug = remote_game['slug']
        sync_required = False
        local_game = pga.get_game_by_field(slug, 'slug')
        if not local_game:
            continue

        if local_game['updated'] and remote_game['updated'] > local_game['updated']:
            # The remote game's info is more recent than the local game
            sync_required = True
        else:
            for key in remote_game.keys():
                if key in local_game.keys() and remote_game[key] and not local_game[key]:
                    # Remote game has data that is missing from the local game.
                    sync_required = True
                    break

        if not sync_required:
            continue

        logger.debug("Syncing details for %s" % slug)
        game_id = pga.add_or_update(
            name=local_game['name'],
            runner=local_game['runner'],
            slug=slug,
            year=remote_game['year'],
            updated=remote_game['updated'],
            steamid=remote_game['steamid']
        )
        updated.add(game_id)

        if not local_game.get('has_custom_banner') and remote_game['banner_url']:
            path = resources.get_icon_path(slug, resources.BANNER)
            resources.download_media(remote_game['banner_url'], path, overwrite=True)
        if not local_game.get('has_custom_icon') and remote_game['icon_url']:
            path = resources.get_icon_path(slug, resources.ICON)
            resources.download_media(remote_game['icon_url'], path, overwrite=True)

    if updated:
        logger.debug("%d games updated", len(updated))
    return updated
Example #36
0
def sync_game_details(remote_library):
    """Update local game details,

    :return: A set of ids of the updated games.
    """
    if not remote_library:
        return set()
    updated = set()

    for remote_game in remote_library:
        slug = remote_game["slug"]
        sync_required = False
        local_game = pga.get_game_by_field(slug, "slug")
        if not local_game:
            continue
        if local_game[
                "updated"] and remote_game["updated"] > local_game["updated"]:
            # The remote game's info is more recent than the local game
            sync_required = True

        if not sync_required:
            continue

        logger.debug("Syncing details for %s", slug)
        game_id = pga.add_or_update(
            id=local_game["id"],
            name=local_game["name"],
            runner=local_game["runner"],
            slug=slug,
            year=remote_game["year"],
            updated=remote_game["updated"],
            steamid=remote_game["steamid"],
        )
        updated.add(game_id)

        if not local_game.get(
                "has_custom_banner") and remote_game["banner_url"]:
            path = resources.get_icon_path(slug, resources.BANNER)
            resources.download_media(remote_game["banner_url"],
                                     path,
                                     overwrite=True)
        if not local_game.get("has_custom_icon") and remote_game["icon_url"]:
            path = resources.get_icon_path(slug, resources.ICON)
            resources.download_media(remote_game["icon_url"],
                                     path,
                                     overwrite=True)

    if updated:
        logger.debug("%d games updated", len(updated))
    return updated
Example #37
0
    def update_existing_games(self, added, updated, first_run=False):
        """Updates the games in the view from the callback of the method
        Still, working on this docstring.
        If the implementation is shit,  the docstring is as well.
        """
        for game_id in updated.difference(added):
            game = pga.get_game_by_field(game_id, "id")
            self.view.update_row(game["id"], game["year"], game["playtime"])

        if first_run:
            self.update_games(added)
            game_slugs = [game["slug"] for game in self.game_list]
            AsyncCall(resources.get_missing_media, self.on_media_returned,
                      game_slugs)
Example #38
0
    def update_existing_games(self, added, updated, first_run=False):
        """???"""
        for game_id in updated.difference(added):
            # XXX this might not work if the game has no 'item' set
            logger.debug("Updating row for ID %s", game_id)
            self.view.update_row(pga.get_game_by_field(game_id, 'id'))

        if first_run:
            logger.info("Setting up view for first run")
            for game_id in added:
                logger.debug("Adding %s", game_id)
                self.add_game_to_view(game_id)
            icons_sync = AsyncCall(self.sync_icons, callback=None)
            self.threads_stoppers.append(icons_sync.stop_request.set)
            self.set_status("")
Example #39
0
    def __init__(self, game_id=None):
        super().__init__()
        self.id = game_id  # pylint: disable=invalid-name
        self.runner = None
        self.config = None

        # Load attributes from database
        game_data = pga.get_game_by_field(game_id, "id")
        self.slug = game_data.get("slug") or ""
        self.runner_name = game_data.get("runner") or ""
        self.directory = game_data.get("directory") or ""
        self.name = game_data.get("name") or ""

        self.game_config_id = game_data.get("configpath") or ""
        self.is_installed = bool(
            game_data.get("installed")) and self.game_config_id
        self.platform = game_data.get("platform") or ""
        self.year = game_data.get("year") or ""
        self.lastplayed = game_data.get("lastplayed") or 0
        self.steamid = game_data.get("steamid") or ""
        self.has_custom_banner = bool(game_data.get("has_custom_banner"))
        self.has_custom_icon = bool(game_data.get("has_custom_icon"))
        try:
            self.playtime = float(game_data.get("playtime") or 0.0)
        except ValueError:
            logger.error("Invalid playtime value %s",
                         game_data.get("playtime"))
            self.playtime = 0.0

        if self.game_config_id:
            self.load_config()
        self.game_thread = None
        self.prelaunch_executor = None
        self.heartbeat = None
        self.killswitch = None
        self.state = self.STATE_IDLE
        self.exit_main_loop = False
        self.xboxdrv_thread = None
        self.game_runtime_config = {}
        self.resolution_changed = False
        self.compositor_disabled = False
        self.stop_compositor = self.start_compositor = ""
        self.original_outputs = None
        self.log_buffer = Gtk.TextBuffer()
        self.log_buffer.create_tag("warning", foreground="red")

        self.timer = Timer()
Example #40
0
    def __init__(self, installer, parent):
        self.error = None
        self.errors = []
        self.target_path = None
        self.parent = parent
        self.reversion_data = {}
        self.game_name = None
        self.game_slug = None
        self.game_files = {}
        self.game_disc = None
        self.cancelled = False
        self.abort_current_task = None
        self.user_inputs = []
        self.steam_data = {}
        self.script = installer['script']
        self.runners_to_install = []
        self.prev_states = []  # Previous states for the Steam installer
        if not self.script:
            return

        self.files = self.script.get('files', [])
        self.version = installer['version']
        self.slug = installer['slug']
        self.year = installer.get('year')
        self.name = installer['name']
        self.runner = installer['runner']
        self.game_name = installer['name']
        self.game_slug = installer['game_slug']
        self.steamid = installer.get('steamid')
        self.requires = self.script.get('requires')
        self.extends = self.script.get('extends')

        if not self.is_valid():
            raise ScriptingError("Invalid script", (self.script, self.errors))

        self._check_dependency()
        if self.creates_game_folder:
            self.target_path = self.get_default_target()

        # If the game is in the library and uninstalled, the first installation
        # updates it
        existing_game = pga.get_game_by_field(self.game_slug, 'slug')
        if existing_game and not existing_game['installed']:
            self.game_id = existing_game['id']
        else:
            self.game_id = None
Example #41
0
 def on_install_clicked(self,
                        *args,
                        game_slug=None,
                        installer_file=None,
                        revision=None):
     """Install a game"""
     if not game_slug:
         game_id = self._get_current_game_id()
         game = pga.get_game_by_field(game_id, 'id')
         game_slug = game.get('slug')
         logger.debug("Installing game %s (%s)" % (game_slug, game_id))
     if not game_slug:
         return
     InstallerDialog(game_slug=game_slug,
                     installer_file=installer_file,
                     revision=revision,
                     parent=self)
Example #42
0
File: game.py Project: Freso/lutris
    def remove(self, from_library=False, from_disk=False):
        if from_disk and self.runner:
            logger.debug("Removing game %s from disk" % self.id)
            self.runner.remove_game_data(game_path=self.directory)

        # Do not keep multiple copies of the same game
        existing_games = pga.get_game_by_field(self.slug, 'slug', all=True)
        if len(existing_games) > 1:
            from_library = True

        if from_library:
            logger.debug("Removing game %s from library" % self.id)
            pga.delete_game(self.id)
        else:
            pga.set_uninstalled(self.id)
        self.config.remove()
        shortcuts.remove_launcher(self.slug, self.id, desktop=True, menu=True)
        return from_library
Example #43
0
    def on_install_clicked(self, *_args, game_slug=None, installer_file=None, revision=None):
        """Install a game"""
        logger.info("Installing %s%s",
                    game_slug if game_slug else installer_file,
                    " (%s)" % revision if revision else '')

        if not game_slug and not installer_file:
            # Install the currently selected game in the UI
            game_id = self._get_current_game_id()
            game = pga.get_game_by_field(game_id, 'id')
            game_slug = game.get('slug')
        if not game_slug and not installer_file:
            return
        return InstallerWindow(game_slug=game_slug,
                               installer_file=installer_file,
                               revision=revision,
                               parent=self,
                               application=self.application)
Example #44
0
    def remove(self, from_library=False, from_disk=False):
        if from_disk and self.runner:
            logger.debug("Removing game %s from disk" % self.id)
            self.runner.remove_game_data(game_path=self.directory)

        # Do not keep multiple copies of the same game
        existing_games = pga.get_game_by_field(self.slug, 'slug', all=True)
        if len(existing_games) > 1:
            from_library = True

        if from_library:
            logger.debug("Removing game %s from library" % self.id)
            pga.delete_game(self.id)
        else:
            pga.set_uninstalled(self.id)
        self.config.remove()
        shortcuts.remove_launcher(self.slug, self.id, desktop=True, menu=True)
        return from_library
Example #45
0
    def __init__(self, game_id=None):
        super().__init__()
        self.id = game_id  # pylint: disable=invalid-name
        self.runner = None
        self.config = None

        # Load attributes from database
        game_data = pga.get_game_by_field(game_id, "id")
        self.slug = game_data.get("slug") or ""
        self.runner_name = game_data.get("runner") or ""
        self.directory = game_data.get("directory") or ""
        self.name = game_data.get("name") or ""

        self.game_config_id = game_data.get("configpath") or ""
        self.is_installed = bool(game_data.get("installed") and self.game_config_id)
        self.platform = game_data.get("platform") or ""
        self.year = game_data.get("year") or ""
        self.lastplayed = game_data.get("lastplayed") or 0
        self.steamid = game_data.get("steamid") or ""
        self.has_custom_banner = bool(game_data.get("has_custom_banner"))
        self.has_custom_icon = bool(game_data.get("has_custom_icon"))
        self.discord_presence = DiscordPresence()
        try:
            self.playtime = float(game_data.get("playtime") or 0.0)
        except ValueError:
            logger.error("Invalid playtime value %s", game_data.get("playtime"))
            self.playtime = 0.0

        if self.game_config_id:
            self.load_config()
        self.game_thread = None
        self.prelaunch_executor = None
        self.heartbeat = None
        self.killswitch = None
        self.state = self.STATE_IDLE
        self.exit_main_loop = False
        self.xboxdrv_thread = None
        self.game_runtime_config = {}
        self.resolution_changed = False
        self.compositor_disabled = False
        self.stop_compositor = self.start_compositor = ""
        self.original_outputs = None
        self._log_buffer = None
        self.timer = Timer()
Example #46
0
def sync_game_details(remote_library):
    """Update local game details,

    :return: A set of ids of the updated games.
    """
    if not remote_library:
        return set()
    updated = set()

    for remote_game in remote_library:
        slug = remote_game["slug"]
        sync_required = False
        local_game = pga.get_game_by_field(slug, "slug")
        if not local_game:
            continue
        if local_game["updated"] and remote_game["updated"] > local_game["updated"]:
            # The remote game's info is more recent than the local game
            sync_required = True

        if not sync_required:
            continue

        logger.debug("Syncing details for %s", slug)
        game_id = pga.add_or_update(
            id=local_game["id"],
            name=local_game["name"],
            runner=local_game["runner"],
            slug=slug,
            year=remote_game["year"],
            updated=remote_game["updated"],
            steamid=remote_game["steamid"],
        )
        updated.add(game_id)

        if not local_game.get("has_custom_banner") and remote_game["banner_url"]:
            path = resources.get_banner_path(slug)
            resources.download_media(remote_game["banner_url"], path, overwrite=True)
        if not local_game.get("has_custom_icon") and remote_game["icon_url"]:
            path = resources.get_icon_path(slug)
            resources.download_media(remote_game["icon_url"], path, overwrite=True)

    if updated:
        logger.debug("%d games updated", len(updated))
    return updated
Example #47
0
    def on_install_clicked(self, *args, game_slug=None, installer_file=None, revision=None):
        """Install a game"""

        installer_desc = game_slug if game_slug else installer_file
        if revision:
            installer_desc += " (%s)" % revision
        logger.info("Installing %s" % installer_desc)

        if not game_slug and not installer_file:
            # Install the currently selected game in the UI
            game_id = self._get_current_game_id()
            game = pga.get_game_by_field(game_id, 'id')
            game_slug = game.get('slug')
        if not game_slug and not installer_file:
            return
        InstallerDialog(game_slug=game_slug,
                        installer_file=installer_file,
                        revision=revision,
                        parent=self)
Example #48
0
    def on_install_clicked(self, *args, game_slug=None, installer_file=None, revision=None):
        """Install a game"""

        installer_desc = game_slug if game_slug else installer_file
        if revision:
            installer_desc += " (%s)" % revision
        logger.info("Installing %s" % installer_desc)

        if not game_slug and not installer_file:
            # Install the currently selected game in the UI
            game_id = self._get_current_game_id()
            game = pga.get_game_by_field(game_id, 'id')
            game_slug = game.get('slug')
        if not game_slug and not installer_file:
            return
        InstallerDialog(game_slug=game_slug,
                        installer_file=installer_file,
                        revision=revision,
                        parent=self)
Example #49
0
    def update_existing_games(self, added, updated, installed, uninstalled, first_run=False):
        for game_id in updated.difference(added):
            self.view.update_row(pga.get_game_by_field(game_id, "id"))

        for game_id in installed.difference(added):
            if not self.view.get_row_by_id(game_id):
                self.view.add_game(game_id)
            self.view.set_installed(Game(game_id))

        for game_id in uninstalled.difference(added):
            self.view.set_uninstalled(game_id)

        self.sidebar_treeview.update()

        if first_run:
            icons_sync = AsyncCall(self.sync_icons, None, stoppable=True)
            self.threads_stoppers.append(icons_sync.stop_request.set)
            self.set_status("Library synced")
            self.update_runtime()
Example #50
0
    def test_can_add_game(self):
        name_entry = self.dlg.name_entry
        name_entry.set_text("Test game")
        self.dlg.runner_dropdown.set_active_id('linux')

        game_box = self.get_game_box()
        exe_box = game_box.get_children()[0].get_children()[0]
        exe_label = exe_box.get_children()[0]
        self.assertEqual(exe_label.get_text(), "Executable")
        test_exe = os.path.abspath(__file__)
        exe_field = exe_box.get_children()[1]
        exe_field.set_file(Gio.File.new_for_path(test_exe))
        exe_field.emit('file-set')
        self.assertEqual(exe_field.get_filename(), test_exe)

        add_button = self.get_buttons().get_children()[1]
        add_button.clicked()

        pga_game = pga.get_game_by_field('test-game', 'slug')
        game = Game(pga_game['id'])
        self.assertEqual(game.name, 'Test game')
        game.remove(from_library=True)
Example #51
0
    def __init__(self, script, parent):
        self.error = None
        self.errors = []
        self.target_path = None
        self.parent = parent
        self.reversion_data = {}
        self.game_name = None
        self.game_slug = None
        self.game_files = {}
        self.game_disc = None
        self.cancelled = False
        self.abort_current_task = None
        self.user_inputs = []
        self.steam_data = {}
        self.script = script
        self.runners_to_install = []
        self.prev_states = []  # Previous states for the Steam installer
        if not self.script:
            return
        if not self.is_valid():
            raise ScriptingError("Invalid script", (self.script, self.errors))

        self.files = self.script.get('files', [])
        self.runner = self.script['runner']
        self.game_name = self.script['name']
        self.game_slug = self.script['game_slug']
        self.requires = self.script.get('requires')
        self.extends = self.script.get('extends')
        self._check_dependency()
        if self.creates_game_folder:
            self.target_path = self.get_default_target()

        # If the game is in the library and uninstalled, the first installation
        # updates it
        existing_game = pga.get_game_by_field(self.game_slug, 'slug')
        if existing_game and not existing_game['installed']:
            self.game_id = existing_game['id']
        else:
            self.game_id = None
Example #52
0
    def play(self):
        launch_info = {}
        launch_info["env"] = self.get_env(os_env=False)

        game_data = pga.get_game_by_field(self.config.game_config_id,
                                          "configpath")

        command = self.launch_args

        if self.is_native:
            if not self.runner_config.get("splore"):
                command.append("-run")
            cartPath = self.cart_path
            if not os.path.exists(cartPath):
                return {"error": "FILE_NOT_FOUND", "file": cartPath}
            command.append(cartPath)

        else:
            command.append("--name")
            command.append(game_data.get("name") + " - PICO-8")

            icon = datapath.get_icon_path(game_data.get("slug"))
            if not os.path.exists(icon):
                icon = os.path.join(datapath.get(),
                                    "media/runner_icons/pico8.png")
            command.append("--icon")
            command.append(icon)

            webargs = {
                "cartridge": self.cart_path,
                "engine": self.engine_path,
                "fullscreen": self.runner_config.get("fullscreen") is True,
            }
            command.append("--execjs")
            command.append("load_config(" + json.dumps(webargs) + ")")

        launch_info["command"] = command
        return launch_info
Example #53
0
    def manually_configure_game(self):
        game_data = pga.get_game_by_field(self.game_slug, "slug")

        if game_data and "slug" in game_data:
            # Game data already exist locally.
            game = Game(game_data["id"])
        else:
            # Try to load game data from remote.
            games = api.get_api_games([self.game_slug])

            if games and len(games) >= 1:
                remote_game = games[0]
                game_data = {
                    "name": remote_game["name"],
                    "slug": remote_game["slug"],
                    "year": remote_game["year"],
                    "updated": remote_game["updated"],
                    "steamid": remote_game["steamid"],
                }
                game = Game(pga.add_game(**game_data))
            else:
                game = None
        AddGameDialog(self.parent, game=game)
Example #54
0
    def __init__(self, game_id=None):
        self.id = game_id
        self.runner = None
        self.game_thread = None
        self.prelaunch_thread = None
        self.heartbeat = None
        self.config = None
        self.killswitch = None
        self.state = self.STATE_IDLE
        self.exit_main_loop = False

        game_data = pga.get_game_by_field(game_id, 'id')
        self.slug = game_data.get('slug') or ''
        self.runner_name = game_data.get('runner') or ''
        self.directory = game_data.get('directory') or ''
        self.name = game_data.get('name') or ''

        self.is_installed = bool(game_data.get('installed')) or False
        self.platform = game_data.get('platform') or ''
        self.year = game_data.get('year') or ''
        self.lastplayed = game_data.get('lastplayed') or 0
        self.game_config_id = game_data.get('configpath') or ''
        self.steamid = game_data.get('steamid') or ''
        self.has_custom_banner = bool(
            game_data.get('has_custom_banner')) or False
        self.has_custom_icon = bool(game_data.get('has_custom_icon')) or False

        self.load_config()
        self.resolution_changed = False
        self.compositor_disabled = False
        self.stop_compositor = self.start_compositor = ""
        self.original_outputs = None
        self.log_buffer = Gtk.TextBuffer()
        self.log_buffer.create_tag("warning", foreground="red")

        self.timer = Timer()
        self.playtime = game_data.get('playtime') or ''
Example #55
0
 def on_steam_game_changed(self, operation, path):
     appmanifest = steam.AppManifest(path)
     runner_name = appmanifest.get_runner_name()
     games = pga.get_game_by_field(appmanifest.steamid,
                                   field='steamid',
                                   all=True)
     if operation == Gio.FileMonitorEvent.DELETED:
         for game in games:
             if game['runner'] == runner_name:
                 steam.mark_as_uninstalled(game)
                 self.view.set_uninstalled(Game(game['id']))
                 break
     elif operation in (Gio.FileMonitorEvent.CHANGED,
                        Gio.FileMonitorEvent.CREATED):
         if not appmanifest.is_installed():
             return
         if runner_name == 'winesteam':
             return
         game_info = None
         for game in games:
             if game['installed'] == 0:
                 game_info = game
             else:
                 # Game is already installed, don't do anything
                 return
         if not game_info:
             game_info = {
                 'name': appmanifest.name,
                 'slug': appmanifest.slug,
             }
         game_id = steam.mark_as_installed(appmanifest.steamid, runner_name,
                                           game_info)
         game_ids = [game['id'] for game in self.game_list]
         if game_id not in game_ids:
             self.add_game_to_view(game_id)
         else:
             self.view.set_installed(Game(game_id))
Example #56
0
 def sync(cls, games, full=False):
     """Import GOG games to the Lutris library"""
     gog_ids = [game.appid for game in games]
     if not gog_ids:
         return ([], [])
     lutris_games = api.get_api_games(gog_ids, query_type="gogid")
     added_games = []
     for game in lutris_games:
         lutris_data = pga.get_game_by_field(game["slug"], field="slug")
         game_data = {
             "name": game["name"],
             "slug": game["slug"],
             "installed": lutris_data["installed"],
             "configpath": lutris_data["configpath"],
             "year": game["year"],
             "updated": game["updated"],
             "gogid": game.get(
                 "gogid"
             ),  # GOG IDs will be added at a later stage in the API
         }
         added_games.append(pga.add_or_update(**game_data))
     if not full:
         return added_games, games
     return added_games, []
Example #57
0
 def update_game_by_id(self, game_id):
     pga_game = pga.get_game_by_field(game_id, "id")
     if pga_game:
         return self.update(pga_game)
     return self.remove_game(game_id)
Example #58
0
    def _write_config(self):
        """Write the game configuration in the DB and config file."""
        if self.extends:
            logger.info(
                'This is an extension to %s, not creating a new game entry',
                self.extends)
            return
        configpath = make_game_config_id(self.slug)
        config_filename = os.path.join(settings.CONFIG_DIR,
                                       "games/%s.yml" % configpath)

        if self.requires:
            # Load the base game config
            required_game = pga.get_game_by_field(self.requires,
                                                  field='installer_slug')
            base_config = LutrisConfig(
                runner_slug=self.runner,
                game_config_id=required_game['configpath'])
            config = base_config.game_level
        else:
            config = {
                'game': {},
            }

        self.game_id = pga.add_or_update(name=self.name,
                                         runner=self.runner,
                                         slug=self.game_slug,
                                         directory=self.target_path,
                                         installed=1,
                                         installer_slug=self.slug,
                                         parent_slug=self.requires,
                                         year=self.year,
                                         steamid=self.steamid,
                                         configpath=configpath,
                                         id=self.game_id)

        game = Game(self.game_id)
        game.set_platform_from_runner()
        game.save()

        logger.debug("Saved game entry %s (%d)", self.game_slug, self.game_id)

        # Config update
        if 'system' in self.script:
            config['system'] = self._substitute_config(self.script['system'])
        if self.runner in self.script and self.script[self.runner]:
            config[self.runner] = self._substitute_config(
                self.script[self.runner])

        # Game options such as exe or main_file can be added at the root of the
        # script as a shortcut, this integrates them into the game config
        # properly
        launcher, launcher_value = self._get_game_launcher()
        if type(launcher_value) == list:
            game_files = []
            for game_file in launcher_value:
                if game_file in self.game_files:
                    game_files.append(self.game_files[game_file])
                else:
                    game_files.append(game_file)
            config['game'][launcher] = game_files
        elif launcher_value:
            if launcher_value in self.game_files:
                launcher_value = (self.game_files[launcher_value])
            elif self.target_path and os.path.exists(
                    os.path.join(self.target_path, launcher_value)):
                launcher_value = os.path.join(self.target_path, launcher_value)
            config['game'][launcher] = launcher_value

        if 'game' in self.script:
            config['game'].update(self.script['game'])
            config['game'] = self._substitute_config(config['game'])

        yaml_config = yaml.safe_dump(config, default_flow_style=False)
        with open(config_filename, "w") as config_file:
            config_file.write(yaml_config)
Example #59
0
    def do_command_line(self, command_line):
        options = command_line.get_options_dict()

        # Use stdout to output logs, only if no command line argument is
        # provided
        argc = len(sys.argv) - 1
        if "-d" in sys.argv or "--debug" in sys.argv:
            argc -= 1
        if not argc:
            # Switch back the log output to stderr (the default in Python)
            # to avoid messing with any output from command line options.

            # Use when targetting Python 3.7 minimum
            # console_handler.setStream(sys.stderr)

            # Until then...
            logger.removeHandler(log.console_handler)
            log.console_handler = logging.StreamHandler(stream=sys.stdout)
            log.console_handler.setFormatter(log.SIMPLE_FORMATTER)
            logger.addHandler(log.console_handler)

        # Set up logger
        if options.contains("debug"):
            log.console_handler.setFormatter(log.DEBUG_FORMATTER)
            logger.setLevel(logging.DEBUG)

        # Text only commands

        # Print Lutris version and exit
        if options.contains("version"):
            executable_name = os.path.basename(sys.argv[0])
            print(executable_name + "-" + settings.VERSION)
            logger.setLevel(logging.NOTSET)
            return 0

        logger.info("Running Lutris %s", settings.VERSION)
        migrate()
        run_all_checks()
        AsyncCall(init_dxvk_versions)

        # List game
        if options.contains("list-games"):
            game_list = pga.get_games()
            if options.contains("installed"):
                game_list = [game for game in game_list if game["installed"]]
            if options.contains("json"):
                self.print_game_json(command_line, game_list)
            else:
                self.print_game_list(command_line, game_list)
            return 0
        # List Steam games
        elif options.contains("list-steam-games"):
            self.print_steam_list(command_line)
            return 0
        # List Steam folders
        elif options.contains("list-steam-folders"):
            self.print_steam_folders(command_line)
            return 0

        # Execute command in Lutris context
        elif options.contains("exec"):
            command = options.lookup_value("exec").get_string()
            self.execute_command(command)
            return 0

        elif options.contains("submit-issue"):
            IssueReportWindow(application=self)
            return 0

        try:
            url = options.lookup_value(GLib.OPTION_REMAINING)
            installer_info = self.get_lutris_action(url)
        except ValueError:
            self._print(command_line, "%s is not a valid URI" % url.get_strv())
            return 1
        game_slug = installer_info["game_slug"]
        action = installer_info["action"]
        revision = installer_info["revision"]

        installer_file = None
        if options.contains("install"):
            installer_file = options.lookup_value("install").get_string()
            installer_file = os.path.abspath(installer_file)
            action = "install"
            if not os.path.isfile(installer_file):
                self._print(command_line, "No such file: %s" % installer_file)
                return 1

        db_game = None
        if game_slug:
            if action == "rungameid":
                # Force db_game to use game id
                self.run_in_background = True
                db_game = pga.get_game_by_field(game_slug, "id")
            elif action == "rungame":
                # Force db_game to use game slug
                self.run_in_background = True
                db_game = pga.get_game_by_field(game_slug, "slug")
            elif action == "install":
                # Installers can use game or installer slugs
                self.run_in_background = True
                db_game = pga.get_game_by_field(
                    game_slug, "slug") or pga.get_game_by_field(
                        game_slug, "installer_slug")
            else:
                # Dazed and confused, try anything that might works
                db_game = (pga.get_game_by_field(game_slug, "id")
                           or pga.get_game_by_field(game_slug, "slug") or
                           pga.get_game_by_field(game_slug, "installer_slug"))

        # Graphical commands
        self.activate()

        if not action:
            if db_game and db_game["installed"]:
                # Game found but no action provided, ask what to do
                dlg = InstallOrPlayDialog(db_game["name"])
                if not dlg.action_confirmed:
                    action = None
                elif dlg.action == "play":
                    action = "rungame"
                elif dlg.action == "install":
                    action = "install"
            elif game_slug or installer_file:
                # No game found, default to install if a game_slug or
                # installer_file is provided
                action = "install"

        if action == "install":
            InstallerWindow(
                game_slug=game_slug,
                installer_file=installer_file,
                revision=revision,
                parent=self.window,
                application=self,
            )
        elif action in ("rungame", "rungameid"):
            if not db_game or not db_game["id"]:
                logger.warning("No game found in library")
                if not self.window.is_visible():
                    self.do_shutdown()
                return 0
            self.launch(Game(db_game["id"]))
        return 0
Example #60
0
    def do_command_line(self, command_line):  # noqa: C901  # pylint: disable=arguments-differ
        # pylint: disable=too-many-locals,too-many-return-statements,too-many-branches
        # pylint: disable=too-many-statements
        # TODO: split into multiple methods to reduce complexity (35)
        options = command_line.get_options_dict()

        # Use stdout to output logs, only if no command line argument is
        # provided
        argc = len(sys.argv) - 1
        if "-d" in sys.argv or "--debug" in sys.argv:
            argc -= 1
        if not argc:
            # Switch back the log output to stderr (the default in Python)
            # to avoid messing with any output from command line options.

            # Use when targetting Python 3.7 minimum
            # console_handler.setStream(sys.stderr)

            # Until then...
            logger.removeHandler(log.console_handler)
            log.console_handler = logging.StreamHandler(stream=sys.stdout)
            log.console_handler.setFormatter(log.SIMPLE_FORMATTER)
            logger.addHandler(log.console_handler)

        # Set up logger
        if options.contains("debug"):
            log.console_handler.setFormatter(log.DEBUG_FORMATTER)
            logger.setLevel(logging.DEBUG)

        # Text only commands

        # Print Lutris version and exit
        if options.contains("version"):
            executable_name = os.path.basename(sys.argv[0])
            print(executable_name + "-" + settings.VERSION)
            logger.setLevel(logging.NOTSET)
            return 0

        logger.info("Running Lutris %s", settings.VERSION)
        migrate()
        run_all_checks()
        AsyncCall(init_dxvk_versions, None)

        # List game
        if options.contains("list-games"):
            game_list = pga.get_games()
            if options.contains("installed"):
                game_list = [game for game in game_list if game["installed"]]
            if options.contains("json"):
                self.print_game_json(command_line, game_list)
            else:
                self.print_game_list(command_line, game_list)
            return 0
        # List Steam games
        if options.contains("list-steam-games"):
            self.print_steam_list(command_line)
            return 0
        # List Steam folders
        if options.contains("list-steam-folders"):
            self.print_steam_folders(command_line)
            return 0

        # Execute command in Lutris context
        if options.contains("exec"):
            command = options.lookup_value("exec").get_string()
            self.execute_command(command)
            return 0

        if options.contains("submit-issue"):
            IssueReportWindow(application=self)
            return 0

        try:
            url = options.lookup_value(GLib.OPTION_REMAINING)
            installer_info = self.get_lutris_action(url)
        except ValueError:
            self._print(command_line, "%s is not a valid URI" % url.get_strv())
            return 1
        game_slug = installer_info["game_slug"]
        action = installer_info["action"]
        revision = installer_info["revision"]

        installer_file = None
        if options.contains("install"):
            installer_file = options.lookup_value("install").get_string()
            if installer_file.startswith(("http:", "https:")):
                try:
                    request = Request(installer_file).get()
                except HTTPError:
                    self._print(command_line,
                                "Failed to download %s" % installer_file)
                    return 1
                try:
                    headers = dict(request.response_headers)
                    file_name = headers["Content-Disposition"].split("=",
                                                                     1)[-1]
                except (KeyError, IndexError):
                    file_name = os.path.basename(installer_file)
                file_path = os.path.join(tempfile.gettempdir(), file_name)
                self._print(
                    command_line,
                    "download %s to %s started" % (installer_file, file_path))
                with open(file_path, 'wb') as dest_file:
                    dest_file.write(request.content)
                installer_file = file_path
                action = "install"
            else:
                installer_file = os.path.abspath(installer_file)
                action = "install"

            if not os.path.isfile(installer_file):
                self._print(command_line, "No such file: %s" % installer_file)
                return 1

        db_game = None
        if game_slug:
            if action == "rungameid":
                # Force db_game to use game id
                self.run_in_background = True
                db_game = pga.get_game_by_field(game_slug, "id")
            elif action == "rungame":
                # Force db_game to use game slug
                self.run_in_background = True
                db_game = pga.get_game_by_field(game_slug, "slug")
            elif action == "install":
                # Installers can use game or installer slugs
                self.run_in_background = True
                db_game = pga.get_game_by_field(
                    game_slug, "slug") or pga.get_game_by_field(
                        game_slug, "installer_slug")
            else:
                # Dazed and confused, try anything that might works
                db_game = (pga.get_game_by_field(game_slug, "id")
                           or pga.get_game_by_field(game_slug, "slug") or
                           pga.get_game_by_field(game_slug, "installer_slug"))

        # Graphical commands
        self.activate()
        self.set_tray_icon()

        if not action:
            if db_game and db_game["installed"]:
                # Game found but no action provided, ask what to do
                dlg = InstallOrPlayDialog(db_game["name"])
                if not dlg.action_confirmed:
                    action = None
                elif dlg.action == "play":
                    action = "rungame"
                elif dlg.action == "install":
                    action = "install"
            elif game_slug or installer_file:
                # No game found, default to install if a game_slug or
                # installer_file is provided
                action = "install"

        if action == "install":
            self.show_window(
                InstallerWindow,
                parent=self.window,
                game_slug=game_slug,
                installer_file=installer_file,
                revision=revision,
            )
        elif action in ("rungame", "rungameid"):
            if not db_game or not db_game["id"]:
                logger.warning("No game found in library")
                if not self.window.is_visible():
                    self.do_shutdown()
                return 0
            self.launch(Game(db_game["id"]))
        return 0