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')
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
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
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']
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
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
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))
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))
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)
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)
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)
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")
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("")
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" )
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)
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)
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")
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' )
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)
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)
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" )
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
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
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)
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)
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)
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()
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)
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)
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)
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
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
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
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)
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("")
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()
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
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
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)
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()
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
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)
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()
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)
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
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
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)
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 ''
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, []
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)
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)
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
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