From bcfcf92bf426e8ce670ea0350c6ca602180eecfd Mon Sep 17 00:00:00 2001 From: Mathieu Comandon Date: Sat, 28 Jan 2023 05:05:15 -0800 Subject: [PATCH] Add support for Battle.net --- lutris/gui/config/common.py | 2 +- lutris/gui/config/runner_box.py | 2 +- lutris/gui/dialogs/__init__.py | 2 - lutris/gui/widgets/sidebar.py | 10 +- lutris/services/__init__.py | 3 +- lutris/services/battlenet.py | 456 +++++----- lutris/services/humblebundle.py | 8 +- lutris/util/battlenet/__init__.py | 3 + lutris/util/battlenet/definitions.py | 155 ++++ lutris/util/battlenet/product_db_pb2.py | 1105 +++++++++++++++++++++++ setup.py | 2 + 11 files changed, 1501 insertions(+), 247 deletions(-) create mode 100644 lutris/util/battlenet/__init__.py create mode 100644 lutris/util/battlenet/definitions.py create mode 100644 lutris/util/battlenet/product_db_pb2.py diff --git a/lutris/gui/config/common.py b/lutris/gui/config/common.py index 3a12838af..74df32fc6 100644 --- a/lutris/gui/config/common.py +++ b/lutris/gui/config/common.py @@ -12,8 +12,8 @@ from lutris.gui import dialogs from lutris.gui.config import DIALOG_HEIGHT, DIALOG_WIDTH from lutris.gui.config.boxes import GameBox, RunnerBox, SystemBox from lutris.gui.dialogs import DirectoryDialog, ErrorDialog, ModelessDialog, QuestionDialog -from lutris.gui.widgets.common import Label, NumberEntry, SlugEntry from lutris.gui.dialogs.delegates import DialogInstallUIDelegate +from lutris.gui.widgets.common import Label, NumberEntry, SlugEntry from lutris.gui.widgets.notifications import send_notification from lutris.gui.widgets.utils import get_pixbuf from lutris.runners import import_runner diff --git a/lutris/gui/config/runner_box.py b/lutris/gui/config/runner_box.py index c51c27127..9b60bae60 100644 --- a/lutris/gui/config/runner_box.py +++ b/lutris/gui/config/runner_box.py @@ -137,4 +137,4 @@ class RunnerBox(Gtk.Box): """Called after the runner is removed""" self.runner_label_box.set_sensitive(False) self.action_alignment.get_children()[0].destroy() - self.action_alignment.add(self.get_action_button()) \ No newline at end of file + self.action_alignment.add(self.get_action_button()) diff --git a/lutris/gui/dialogs/__init__.py b/lutris/gui/dialogs/__init__.py index de757470a..a84ae8154 100644 --- a/lutris/gui/dialogs/__init__.py +++ b/lutris/gui/dialogs/__init__.py @@ -565,8 +565,6 @@ class MoveDialog(ModelessDialog): self.destroy() - - class HumbleBundleCookiesDialog(ModalDialog): def __init__(self, parent=None): super().__init__(_("Humble Bundle Cookie Authentication"), parent) diff --git a/lutris/gui/widgets/sidebar.py b/lutris/gui/widgets/sidebar.py index b739215bb..0497d5d06 100644 --- a/lutris/gui/widgets/sidebar.py +++ b/lutris/gui/widgets/sidebar.py @@ -127,9 +127,15 @@ class ServiceSidebarRow(SidebarRow): def get_actions(self): """Return the definition of buttons to be added to the row""" - return [ + displayed_buttons = [] + if self.service.is_launchable(): + displayed_buttons.append( + ("media-playback-start-symbolic", _("Run"), self.on_service_run, "run") + ) + displayed_buttons.append( ("view-refresh-symbolic", _("Reload"), self.on_refresh_clicked, "refresh") - ] + ) + return displayed_buttons def on_service_run(self, button): """Run a launcher associated with a service""" diff --git a/lutris/services/__init__.py b/lutris/services/__init__.py index 817943f58..d9867ec04 100644 --- a/lutris/services/__init__.py +++ b/lutris/services/__init__.py @@ -33,7 +33,8 @@ def get_services(): "egs": EpicGamesStoreService, "origin": OriginService, "ubisoft": UbisoftConnectService, - "amazon": AmazonService + "amazon": AmazonService, + "battlenet": BattleNetService, } if not LINUX_SYSTEM.is_flatpak: _services["xdg"] = XDGService diff --git a/lutris/services/battlenet.py b/lutris/services/battlenet.py index b100de968..51a7204bd 100644 --- a/lutris/services/battlenet.py +++ b/lutris/services/battlenet.py @@ -1,252 +1,236 @@ -"""Battle.net service. -Not ready yet. -""" -import pickle +"""Battle.net service""" +import json +import os from gettext import gettext as _ -from urllib.parse import parse_qs, urlparse -import requests +from gi.repository import Gio -from lutris.services.base import OnlineService +from lutris import settings +from lutris.config import LutrisConfig, write_game_config +from lutris.database.games import add_game, get_game_by_field +from lutris.database.services import ServiceGameCollection +# from lutris.util import system +from lutris.game import Game +from lutris.services.base import BaseService +from lutris.services.service_game import ServiceGame +from lutris.services.service_media import ServiceMedia +from lutris.util.battlenet.definitions import ProductDbInfo +from lutris.util.battlenet.product_db_pb2 import ProductDb from lutris.util.log import logger -CLIENT_ID = "6cb41a854631426c8a74d4084c4d61f2" -CLIENT_SECRET = "FFwxmMBGtEqPydyi9FMhj1zIvlJrBTE1" -REDIRECT_URI = "https://lutris.net" -USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0" +GAME_IDS = { + 's1': ('s1', 'StarCraft', 'S1', 'starcraft-remastered'), + 's2': ('s2', 'StarCraft II', 'S2', 'starcraft-ii'), + 'wow': ('wow', 'World of Warcraft', 'WoW', 'world-of-warcraft'), + 'wow_classic': ('wow_classic', 'World of Warcraft Classic', 'WoW_wow_classic', 'world-of-warcraft-classic'), + 'pro': ('pro', 'Overwatch 2', 'Pro', 'overwatch-2'), + 'w3': ('w3', 'Warcraft III', 'W3', 'warcraft-iii-reforged'), + 'hs_beta': ('hs_beta', 'Hearthstone', 'WTCG', 'hearthstone'), + 'heroes': ('heroes', 'Heroes of the Storm', 'Hero', 'heroes-of-the-storm'), + 'd3cn': ('d3cn', '暗黑破壞神III', 'D3CN', 'diablo-iii'), + 'diablo3': ('diablo3', 'Diablo III', 'D3', 'diablo-iii'), + 'viper': ('viper', 'Call of Duty: Black Ops 4', 'VIPR', 'call-of-duty-black-ops-4'), + 'odin': ('odin', 'Call of Duty: Modern Warfare', 'ODIN', 'call-of-duty-modern-warfare'), + 'lazarus': ('lazarus', 'Call of Duty: MW2 Campaign Remastered', 'LAZR', + 'call-of-duty-modern-warfare-2-campaign-remastered'), + 'zeus': ('zeus', 'Call of Duty: Black Ops Cold War', 'ZEUS', 'call-of-duty-black-ops-cold-war'), + 'rtro': ('rtro', 'Blizzard Arcade Collection', 'RTRO', 'blizzard-arcade-collection'), + 'wlby': ('wlby', 'Crash Bandicoot 4: It\'s About Time', 'WLBY', 'crash-bandicoot-4-its-about-time'), + 'osi': ('osi', 'Diablo® II: Resurrected', 'OSI', 'diablo-2-ressurected'), + 'fore': ('fore', 'Call of Duty: Vanguard', 'FORE', 'call-of-duty-vanguard'), + 'd2': ('d2', 'Diablo® II', 'Diablo II', 'diablo-ii'), + 'd2LOD': ('d2LOD', 'Diablo® II: Lord of Destruction®', 'Diablo II', 'diablo-ii-lord-of-destruction'), + 'w3ROC': ('w3ROC', 'Warcraft® III: Reign of Chaos', 'Warcraft III', "warcraft-iii-reign-of-chaos"), + 'w3tft': ('w3tft', 'Warcraft® III: The Frozen Throne®', 'Warcraft III', "warcraft-iii-the-frozen-throne"), + 'sca': ('sca', 'StarCraft® Anthology', 'Starcraft', 'starcraft') +} -class InvalidCredentials(Exception): - pass +class BattleNetCover(ServiceMedia): + service = 'battlenet' + size = (176, 234) + file_pattern = "%s.jpg" + file_format = "jpeg" + dest_path = os.path.join(settings.CACHE_DIR, "battlenet/coverart") + api_field = 'coverart' -def _found_region(cookies): - try: - for cookie in cookies: - if cookie['name'] == 'JSESSIONID': - _region = cookie['domain'].split('.')[0] - # 4th region - chinese uses different endpoints, not covered by current plugin - if _region.lower() in ['eu', 'us', 'kr']: - return _region - raise ValueError(f'Unknown region {_region}') - raise ValueError('JSESSIONID cookie not found') - except Exception: - return 'eu' +class BattleNetGame(ServiceGame): + """Game from Battle.net""" + service = "battlenet" + runner = "wine" + installer_slug = "battlenet" + + @classmethod + def create(cls, blizzard_game): + """Create a service game from an entry from the Dolphin cache""" + service_game = cls() + service_game.name = blizzard_game[1] + service_game.appid = blizzard_game[0] + service_game.slug = blizzard_game[3] + service_game.details = json.dumps({ + "id": blizzard_game[0], + "name": blizzard_game[1], + "slug": blizzard_game[3], + "product_code": blizzard_game[2], + "coverart": "https://lutris.net/games/cover/%s.jpg" % blizzard_game[3] + }) + return service_game -def guess_region(local_client): - """ - 1. read the consts.py - 2. try read the battlenet db OR config get the region info. - 3. try query https://www.blizzard.com/en-us/user - 4. failed return "" - """ - try: - if local_client._load_local_files(): - if local_client.config_parser.region: - return local_client.config_parser.region.lower() - - if local_client.database_parser.region: - return local_client.database_parser.region.lower() - - response = requests.get('https://www.blizzard.com/en-us/user', timeout=10) - assert response.status_code == 200 - return response.json()['region'].lower() - except Exception as e: - logger.error('%s', e) - return "" - - -class BattleNetClient(): - def __init__(self, plugin): - self._plugin = plugin - self.user_details = None - self._region = None - self.session = None - self.creds = None - self.timeout = 40.0 - self.attempted_to_set_battle_tag = None - self.auth_data = {} - - def is_authenticated(self): - return self.session is not None - - def shutdown(self): - if self.session: - self.session.close() - self.session = None - - def process_stored_credentials(self, stored_credentials): - auth_data = { - "cookie_jar": pickle.loads(bytes.fromhex(stored_credentials['cookie_jar'])), - "access_token": stored_credentials['access_token'], - "region": stored_credentials['region'] if 'region' in stored_credentials else 'eu' - } - - # set default user_details data from cache - if 'user_details_cache' in stored_credentials: - self.user_details = stored_credentials['user_details_cache'] - self.auth_data = auth_data - return auth_data - - def get_auth_data_login(self, cookie_jar, credentials): - code = parse_qs(urlparse(credentials['end_uri']).query)["code"][0] - - s = requests.Session() - url = f"{self.blizzard_oauth_url}/token" - data = { - "grant_type": "authorization_code", - "redirect_uri": REDIRECT_URI, - "client_id": CLIENT_ID, - "client_secret": CLIENT_SECRET, - "code": code - } - response = s.post(url, data=data) - response.raise_for_status() - result = response.json() - access_token = result["access_token"] - self.auth_data = {"cookie_jar": cookie_jar, "access_token": access_token, "region": self.region} - return self.auth_data - - # NOTE: use user data to present usertag/name to Galaxy, if this token expires and plugin cannot refresh it - # use stored usertag/name if token validation fails, this is temporary solution, as we do not need that - # endpoint for nothing else at this moment - def validate_auth_status(self, auth_status): - if 'error' in auth_status: - if not self.user_details: - raise InvalidCredentials() - return False - if not self.user_details: - raise InvalidCredentials() - if not ("authorities" in auth_status and "IS_AUTHENTICATED_FULLY" in auth_status["authorities"]): - raise InvalidCredentials() - return True - - def parse_user_details(self): - if 'battletag' in self.user_details and 'id' in self.user_details: - return (self.user_details["id"], self.user_details["battletag"]) - raise InvalidCredentials() - - def authenticate_using_login(self): - _URI = ( - f'{self.blizzard_oauth_url}/authorize?response_type=code&' - f'client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope=wow.profile+sc2.profile' - ) - return { - "window_title": "Login to Battle.net", - "window_width": 540, - "window_height": 700, - "start_uri": _URI, - "end_uri_regex": r"(.*logout&app=oauth.*)|(^http://friendsofgalaxy\.com.*)" - } - - def parse_auth_after_setting_battletag(self): - self.creds["user_details_cache"] = self.user_details - try: - battletag = self.user_details["battletag"] - except KeyError as ex: - raise InvalidCredentials() from ex - self._plugin.store_credentials(self.creds) - return (self.user_details["id"], battletag) - - def parse_cookies(self, cookies): - if not self.region: - self.region = _found_region(cookies) - new_cookies = {cookie["name"]: cookie["value"] for cookie in cookies} - return requests.cookies.cookiejar_from_dict(new_cookies) - - def set_credentials(self): - self.creds = { - "cookie_jar": pickle.dumps(self.auth_data["cookie_jar"]).hex(), - "access_token": self.auth_data["access_token"], - "user_details_cache": self.user_details, - "region": self.auth_data["region"] - } - - def parse_battletag(self): - try: - battletag = self.user_details["battletag"] - except KeyError: - st_parameter = requests.utils.dict_from_cookiejar(self.auth_data["cookie_jar"])["BA-tassadar"] - start_uri = f'{self.blizzard_battlenet_login_url}/flow/' \ - f'app.app?step=login&ST={st_parameter}&app=app&cr=true' - auth_params = { - "window_title": "Login to Battle.net", - "window_width": 540, - "window_height": 700, - "start_uri": start_uri, - "end_uri_regex": r".*accountName.*" - } - self.attempted_to_set_battle_tag = True - return auth_params - - self._plugin.store_credentials(self.creds) - return (self.user_details["id"], battletag) - - async def create_session(self): - self.session = requests.Session() - self.session.cookies = self.auth_data["cookie_jar"] - self.region = self.auth_data["region"] - self.session.max_redirects = 300 - self.session.headers = { - "Authorization": f"Bearer {self.auth_data['access_token']}", - "User-Agent": USER_AGENT - } - - def refresh_credentials(self): - creds = { - "cookie_jar": pickle.dumps(self.session.cookies).hex(), - "access_token": self.auth_data["access_token"], - "region": self.auth_data["region"], - "user_details_cache": self.user_details - } - self._plugin.store_credentials(creds) - - @property - def region(self): - if self._region is None: - self._region = guess_region(self._plugin.local_client) - return self._region - - @region.setter - def region(self, value): - self._region = value - - @property - def blizzard_accounts_url(self): - if self.region == 'cn': - return "https://account.blizzardgames.cn" - return f"https://{self.region}.account.blizzard.com" - - @property - def blizzard_oauth_url(self): - if self.region == 'cn': - return "https://www.battlenet.com.cn/oauth" - return f"https://{self.region}.battle.net/oauth" - - @property - def blizzard_api_url(self): - if self.region == 'cn': - return "https://gateway.battlenet.com.cn" - return f"https://{self.region}.api.blizzard.com" - - @property - def blizzard_battlenet_download_url(self): - if self.region == 'cn': - return "https://cn.blizzard.com/zh-cn/apps/battle.net/desktop" - return "https://www.blizzard.com/apps/battle.net/desktop" - - @property - def blizzard_battlenet_login_url(self): - if self.region == 'cn': - return 'https://www.battlenet.com.cn/login/zh' - return f'https://{self.region}.battle.net/login/en' - - -class BattleNetService(OnlineService): +class BattleNetService(BaseService): """Service class for Battle.net""" id = "battlenet" name = _("Battle.net") icon = "battlenet" - medias = {} + runner = "wine" + medias = { + "coverart": BattleNetCover + } + default_format = "coverart" + client_installer = "battlenet" + cookies_path = os.path.join(settings.CACHE_DIR, ".bnet.auth") + cache_path = os.path.join(settings.CACHE_DIR, "bnet-library.json") + redirect_uri = "https://lutris.net" + + @property + def battlenet_config_path(self): + return "" + + def load(self): + games = [BattleNetGame.create(game) for game in GAME_IDS.values()] + for game in games: + game.save() + return games + + def add_installed_games(self): + """Scan an existing EGS install for games""" + bnet_game = get_game_by_field(self.client_installer, "slug") + if not bnet_game: + logger.error("Battle.net is not installed in Lutris") + return + bnet_prefix = bnet_game["directory"].split("drive_c")[0] + parser = BlizzardProductDbParser(bnet_prefix) + for game in parser.games: + self.install_from_battlenet(bnet_game, game) + + def install_from_battlenet(self, bnet_game, game): + app_id = game.ngdp + logger.debug("Installing Battle.net game %s", app_id) + service_game = ServiceGameCollection.get_game("battlenet", app_id) + if not service_game: + logger.error("Aborting install, %s is not present in the game library.", app_id) + return + lutris_game_id = service_game["slug"] + "-" + self.id + existing_game = get_game_by_field(lutris_game_id, "installer_slug") + if existing_game: + return + game_config = LutrisConfig(game_config_id=bnet_game["configpath"]).game_level + game_config["game"]["args"] = '--exec="launch %s"' % game.ngdp + configpath = write_game_config(lutris_game_id, game_config) + game_id = add_game( + name=service_game["name"], + runner=bnet_game["runner"], + slug=service_game["slug"], + directory=bnet_game["directory"], + installed=1, + installer_slug=lutris_game_id, + configpath=configpath, + service=self.id, + service_id=app_id, + platform="Windows" + ) + return game_id + + def generate_installer(self, db_game, egs_db_game): + egs_game = Game(egs_db_game["id"]) + egs_exe = egs_game.config.game_config["exe"] + if not os.path.isabs(egs_exe): + egs_exe = os.path.join(egs_game.config.game_config["prefix"], egs_exe) + return { + "name": db_game["name"], + "version": self.name, + "slug": db_game["slug"] + "-" + self.id, + "game_slug": db_game["slug"], + "runner": self.runner, + "appid": db_game["appid"], + "script": { + "requires": self.client_installer, + "game": { + "args": '--exec="launch %s"' % db_game["appid"], + }, + "installer": [ + {"task": { + "name": "wineexec", + "executable": egs_exe, + "args": '--exec="install %s"' % db_game["appid"], + "prefix": egs_game.config.game_config["prefix"], + "description": ( + "Battle.net will now open. Please launch " + "the installation of %s then close Battle.net " + "once the game has been downloaded." % db_game["name"] + ) + }} + ] + } + } + + def install(self, db_game): + bnet_game = get_game_by_field(self.client_installer, "slug") + application = Gio.Application.get_default() + application.show_installer_window( + [self.generate_installer(db_game, bnet_game)], + service=self, + appid=db_game["appid"] + ) + + +class BlizzardProductDbParser: + # Adapted from DatabaseParser in https://github.com/bartok765/galaxy_blizzard_plugin + NOT_GAMES = ('bna', 'agent') + PRODUCT_DB_PATH = "/drive_c/ProgramData/Battle.net/Agent/product.db" + + def __init__(self, prefix_path): + self.data = self.load_product_db(prefix_path + self.PRODUCT_DB_PATH) + self.products = {} + self._region = '' + self.parse() + + @property + def region(self): + return self._region + + @staticmethod + def load_product_db(product_db_path): + with open(product_db_path, 'rb') as f: + pdb = f.read() + return pdb + + @property + def games(self): + if self.products: + return [v for k, v in self.products.items() if k not in self.NOT_GAMES] + return [] + + def parse(self): + self.products = {} + database = ProductDb() + database.ParseFromString(self.data) + + for product_install in database.product_installs: # pylint: disable=no-member + # process region + if product_install.product_code in ['agent', + 'bna'] and not self.region: + self._region = product_install.settings.play_region + + ngdp_code = product_install.product_code + uninstall_tag = product_install.uid + install_path = product_install.settings.install_path + playable = product_install.cached_product_state.base_product_state.playable + version = product_install.cached_product_state.base_product_state.current_version_str + installed = product_install.cached_product_state.base_product_state.installed + + self.products[ngdp_code] = ProductDbInfo( + uninstall_tag, ngdp_code, install_path, version, playable, installed + ) diff --git a/lutris/services/humblebundle.py b/lutris/services/humblebundle.py index d72bd103c..742e6d37b 100644 --- a/lutris/services/humblebundle.py +++ b/lutris/services/humblebundle.py @@ -8,9 +8,9 @@ from gi.repository import Gtk from lutris import settings from lutris.exceptions import UnavailableGameError +from lutris.gui.dialogs import HumbleBundleCookiesDialog, QuestionDialog from lutris.installer import AUTO_ELF_EXE, AUTO_WIN32_EXE from lutris.installer.installer_file import InstallerFile -from lutris.gui.dialogs import QuestionDialog, HumbleBundleCookiesDialog from lutris.services.base import OnlineService from lutris.services.service_game import ServiceGame from lutris.services.service_media import ServiceMedia @@ -84,9 +84,9 @@ class HumbleBundleService(OnlineService): dialog = QuestionDialog({ "title": _("Workaround for Humble Bundle authentication"), "question": _("Humble Bundle is restricting API calls from software like Lutris and GameHub.\n" - "Authentication to the service will likely fail.\n" - "There is a workaround involving copying cookies " - "from Firefox, do you want to do this instead?"), + "Authentication to the service will likely fail.\n" + "There is a workaround involving copying cookies " + "from Firefox, do you want to do this instead?"), "parent": parent }) if dialog.result == Gtk.ResponseType.YES: diff --git a/lutris/util/battlenet/__init__.py b/lutris/util/battlenet/__init__.py new file mode 100644 index 000000000..d7cac88df --- /dev/null +++ b/lutris/util/battlenet/__init__.py @@ -0,0 +1,3 @@ +# Code in this package from the GOG Galaxy integration for Battle.net +# https://github.com/bartok765/galaxy_blizzard_plugin +# All credits go to bartok765 and contributors diff --git a/lutris/util/battlenet/definitions.py b/lutris/util/battlenet/definitions.py new file mode 100644 index 000000000..27eb4ea71 --- /dev/null +++ b/lutris/util/battlenet/definitions.py @@ -0,0 +1,155 @@ +import dataclasses as dc +import json +from typing import List, Optional + +import requests + + +class DataclassJSONEncoder(json.JSONEncoder): + def default(self, o): + if dc.is_dataclass(o): + return dc.asdict(o) + return super().default(o) + + +@dc.dataclass +class WebsiteAuthData(object): + cookie_jar: requests.cookies.RequestsCookieJar + access_token: str + region: str + + +@dc.dataclass(frozen=True) +class BlizzardGame: + uid: str + name: str + family: str + + +@dc.dataclass(frozen=True) +class ClassicGame(BlizzardGame): + registry_path: Optional[str] = None + registry_installation_key: Optional[str] = None + exe: Optional[str] = None + bundle_id: Optional[str] = None + + +@dc.dataclass +class RegionalGameInfo: + uid: str + try_for_free: bool + + +@dc.dataclass +class ConfigGameInfo(object): + uid: str + uninstall_tag: Optional[str] + last_played: Optional[str] + + +@dc.dataclass +class ProductDbInfo(object): + uninstall_tag: str + ngdp: str = '' + install_path: str = '' + version: str = '' + playable: bool = False + installed: bool = False + + +class Singleton(type): + _instances = {} # type: ignore + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] + + +class _Blizzard(object, metaclass=Singleton): + TITLE_ID_MAP = { + 21297: RegionalGameInfo('s1', True), + 21298: RegionalGameInfo('s2', True), + 5730135: RegionalGameInfo('wow', True), + 5272175: RegionalGameInfo('prometheus', False), + 22323: RegionalGameInfo('w3', False), + 1146311730: RegionalGameInfo('destiny2', False), + 1465140039: RegionalGameInfo('hs_beta', True), + 1214607983: RegionalGameInfo('heroes', True), + 17459: RegionalGameInfo('diablo3', True), + 1447645266: RegionalGameInfo('viper', False), + 1329875278: RegionalGameInfo('odin', True), + 1279351378: RegionalGameInfo('lazarus', False), + 1514493267: RegionalGameInfo('zeus', False), + 1381257807: RegionalGameInfo('rtro', False), + 1464615513: RegionalGameInfo('wlby', False), + 5198665: RegionalGameInfo('osi', False), + 1179603525: RegionalGameInfo('fore', False) + } + TITLE_ID_MAP_CN = { + **TITLE_ID_MAP, + 17459: RegionalGameInfo('d3cn', False) + } + BATTLENET_GAMES = [ + BlizzardGame('s1', 'StarCraft', 'S1'), + BlizzardGame('s2', 'StarCraft II', 'S2'), + BlizzardGame('wow', 'World of Warcraft', 'WoW'), + BlizzardGame('wow_classic', 'World of Warcraft Classic', 'WoW_wow_classic'), + BlizzardGame('prometheus', 'Overwatch', 'Pro'), + BlizzardGame('w3', 'Warcraft III', 'W3'), + BlizzardGame('hs_beta', 'Hearthstone', 'WTCG'), + BlizzardGame('heroes', 'Heroes of the Storm', 'Hero'), + BlizzardGame('d3cn', '暗黑破壞神III', 'D3CN'), + BlizzardGame('diablo3', 'Diablo III', 'D3'), + BlizzardGame('viper', 'Call of Duty: Black Ops 4', 'VIPR'), + BlizzardGame('odin', 'Call of Duty: Modern Warfare', 'ODIN'), + BlizzardGame('lazarus', 'Call of Duty: MW2 Campaign Remastered', 'LAZR'), + BlizzardGame('zeus', 'Call of Duty: Black Ops Cold War', 'ZEUS'), + BlizzardGame('rtro', 'Blizzard Arcade Collection', 'RTRO'), + BlizzardGame('wlby', 'Crash Bandicoot 4: It\'s About Time', 'WLBY'), + BlizzardGame('osi', 'Diablo® II: Resurrected', 'OSI'), + BlizzardGame('fore', 'Call of Duty: Vanguard', 'FORE') + ] + CLASSIC_GAMES = [ + ClassicGame('d2', 'Diablo® II', 'Diablo II', 'Diablo II', 'DisplayIcon', "Game.exe", "com.blizzard.diabloii"), + ClassicGame('d2LOD', 'Diablo® II: Lord of Destruction®', 'Diablo II'), # TODO exe and bundleid + ClassicGame('w3ROC', 'Warcraft® III: Reign of Chaos', 'Warcraft III', 'Warcraft III', + 'InstallLocation', 'Warcraft III.exe', 'com.blizzard.WarcraftIII'), + ClassicGame('w3tft', 'Warcraft® III: The Frozen Throne®', 'Warcraft III', 'Warcraft III', + 'InstallLocation', 'Warcraft III.exe', 'com.blizzard.WarcraftIII'), + ClassicGame('sca', 'StarCraft® Anthology', 'Starcraft', 'StarCraft') # TODO exe and bundleid + ] + + def __init__(self): + self._games = {game.uid: game for game in self.BATTLENET_GAMES + self.CLASSIC_GAMES} + + def __getitem__(self, key: str) -> BlizzardGame: + """ + :param key: str uid (eg. "prometheus") + :returns: game by `key` + """ + return self._games[key] + + def game_by_title_id(self, title_id: int, cn: bool) -> BlizzardGame: + """ + :param cn: flag if china game definitions should be search though + :raises KeyError: when unknown title_id for given region + """ + if cn: + regional_info = self.TITLE_ID_MAP_CN[title_id] + else: + regional_info = self.TITLE_ID_MAP[title_id] + return self[regional_info.uid] + + def try_for_free_games(self, cn: bool) -> List[BlizzardGame]: + """ + :param cn: flag if china game definitions should be search though + """ + return [ + self[info.uid] for info + in (self.TITLE_ID_MAP_CN if cn else self.TITLE_ID_MAP).values() + if info.try_for_free + ] + + +Blizzard = _Blizzard() diff --git a/lutris/util/battlenet/product_db_pb2.py b/lutris/util/battlenet/product_db_pb2.py new file mode 100644 index 000000000..153007cee --- /dev/null +++ b/lutris/util/battlenet/product_db_pb2.py @@ -0,0 +1,1105 @@ +# noqa +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: product_db.proto + +import sys + +_b = sys.version_info[0] < 3 and (lambda x: x) or (lambda x: x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import enum_type_wrapper + +# ~ from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='product_db.proto', + package='', + syntax='proto3', + serialized_pb=_b('\n\x10product_db.proto\"D\n\x0fLanguageSetting\x12\x10\n\x08language\x18\x01 \x01(\t\x12\x1f\n\x06option\x18\x02 \x01(\x0e\x32\x0f.LanguageOption\"\xdb\x02\n\x0cUserSettings\x12\x14\n\x0cinstall_path\x18\x01 \x01(\t\x12\x13\n\x0bplay_region\x18\x02 \x01(\t\x12)\n\x10\x64\x65sktop_shortcut\x18\x03 \x01(\x0e\x32\x0f.ShortcutOption\x12+\n\x12startmenu_shortcut\x18\x04 \x01(\x0e\x32\x0f.ShortcutOption\x12/\n\x11language_settings\x18\x05 \x01(\x0e\x32\x14.LanguageSettingType\x12\x1e\n\x16selected_text_language\x18\x06 \x01(\t\x12 \n\x18selected_speech_language\x18\x07 \x01(\t\x12#\n\tlanguages\x18\x08 \x03(\x0b\x32\x10.LanguageSetting\x12\x19\n\x11gfx_override_tags\x18\t \x01(\t\x12\x15\n\rversionbranch\x18\n \x01(\t\"Q\n\x10InstallHandshake\x12\x0f\n\x07product\x18\x01 \x01(\t\x12\x0b\n\x03uid\x18\x02 \x01(\t\x12\x1f\n\x08settings\x18\x03 \x01(\x0b\x32\r.UserSettings\"3\n\x0b\x42uildConfig\x12\x0e\n\x06region\x18\x01 \x01(\t\x12\x14\n\x0c\x62uild_config\x18\x02 \x01(\t\"\xf4\x02\n\x10\x42\x61seProductState\x12\x11\n\tinstalled\x18\x01 \x01(\x08\x12\x10\n\x08playable\x18\x02 \x01(\x08\x12\x17\n\x0fupdate_complete\x18\x03 \x01(\x08\x12%\n\x1d\x62\x61\x63kground_download_available\x18\x04 \x01(\x08\x12$\n\x1c\x62\x61\x63kground_download_complete\x18\x05 \x01(\x08\x12\x17\n\x0f\x63urrent_version\x18\x06 \x01(\t\x12\x1b\n\x13\x63urrent_version_str\x18\x07 \x01(\t\x12,\n\x16installed_build_config\x18\x08 \x03(\x0b\x32\x0c.BuildConfig\x12\x36\n background_download_build_config\x18\t \x03(\x0b\x32\x0c.BuildConfig\x12\x16\n\x0e\x64\x65\x63ryption_key\x18\n \x01(\t\x12!\n\x19\x63ompleted_install_actions\x18\x0b \x03(\t\"h\n\x10\x42\x61\x63kfillProgress\x12\x10\n\x08progress\x18\x01 \x01(\x01\x12\x1a\n\x12\x62\x61\x63kgrounddownload\x18\x02 \x01(\x08\x12\x0e\n\x06paused\x18\x03 \x01(\x08\x12\x16\n\x0e\x64ownload_limit\x18\x04 \x01(\x04\"\"\n\x0eRepairProgress\x12\x10\n\x08progress\x18\x01 \x01(\x01\"\x8b\x01\n\x0eUpdateProgress\x12\x1a\n\x12last_disc_set_used\x18\x01 \x01(\t\x12\x10\n\x08progress\x18\x02 \x01(\x01\x12\x14\n\x0c\x64isc_ignored\x18\x03 \x01(\x08\x12\x19\n\x11total_to_download\x18\x04 \x01(\x04\x12\x1a\n\x12\x64ownload_remaining\x18\x05 \x01(\x04\"\xc5\x01\n\x12\x43\x61\x63hedProductState\x12-\n\x12\x62\x61se_product_state\x18\x01 \x01(\x0b\x32\x11.BaseProductState\x12,\n\x11\x62\x61\x63kfill_progress\x18\x02 \x01(\x0b\x32\x11.BackfillProgress\x12(\n\x0frepair_progress\x18\x03 \x01(\x0b\x32\x0f.RepairProgress\x12(\n\x0fupdate_progress\x18\x04 \x01(\x0b\x32\x0f.UpdateProgress\"K\n\x11ProductOperations\x12$\n\x10\x61\x63tive_operation\x18\x01 \x01(\x0e\x32\n.Operation\x12\x10\n\x08priority\x18\x02 \x01(\x04\"\xb7\x01\n\x0eProductInstall\x12\x0b\n\x03uid\x18\x01 \x01(\t\x12\x14\n\x0cproduct_code\x18\x02 \x01(\t\x12\x1f\n\x08settings\x18\x03 \x01(\x0b\x32\r.UserSettings\x12\x31\n\x14\x63\x61\x63hed_product_state\x18\x04 \x01(\x0b\x32\x13.CachedProductState\x12.\n\x12product_operations\x18\x05 \x01(\x0b\x32\x12.ProductOperations\"O\n\rProductConfig\x12\x14\n\x0cproduct_code\x18\x01 \x01(\t\x12\x15\n\rmetadata_hash\x18\x02 \x01(\t\x12\x11\n\ttimestamp\x18\x03 \x01(\t\"?\n\rActiveProcess\x12\x14\n\x0cprocess_name\x18\x01 \x01(\t\x12\x0b\n\x03pid\x18\x02 \x01(\x05\x12\x0b\n\x03uri\x18\x03 \x03(\t\"B\n\x10\x44ownloadSettings\x12\x16\n\x0e\x64ownload_limit\x18\x01 \x01(\x05\x12\x16\n\x0e\x62\x61\x63kfill_limit\x18\x02 \x01(\x05\"\xe3\x01\n\tProductDb\x12)\n\x10product_installs\x18\x01 \x03(\x0b\x32\x0f.ProductInstall\x12*\n\x0f\x61\x63tive_installs\x18\x02 \x03(\x0b\x32\x11.InstallHandshake\x12(\n\x10\x61\x63tive_processes\x18\x03 \x03(\x0b\x32\x0e.ActiveProcess\x12\'\n\x0fproduct_configs\x18\x04 \x03(\x0b\x32\x0e.ProductConfig\x12,\n\x11\x64ownload_settings\x18\x05 \x01(\x0b\x32\x11.DownloadSettings*q\n\x0eLanguageOption\x12\x13\n\x0fLANGOPTION_NONE\x10\x00\x12\x13\n\x0fLANGOPTION_TEXT\x10\x01\x12\x15\n\x11LANGOPTION_SPEECH\x10\x02\x12\x1e\n\x1aLANGOPTION_TEXT_AND_SPEECH\x10\x03*u\n\x13LanguageSettingType\x12\x14\n\x10LANGSETTING_NONE\x10\x00\x12\x16\n\x12LANGSETTING_SINGLE\x10\x01\x12\x16\n\x12LANGSETTING_SIMPLE\x10\x02\x12\x18\n\x14LANGSETTING_ADVANCED\x10\x03*N\n\x0eShortcutOption\x12\x11\n\rSHORTCUT_NONE\x10\x00\x12\x11\n\rSHORTCUT_USER\x10\x01\x12\x16\n\x12SHORTCUT_ALL_USERS\x10\x02*P\n\tOperation\x12\r\n\tOP_UPDATE\x10\x00\x12\x0f\n\x0bOP_BACKFILL\x10\x01\x12\r\n\tOP_REPAIR\x10\x02\x12\x14\n\x07OP_NONE\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x62\x06proto3') # noqa +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +_LANGUAGEOPTION = _descriptor.EnumDescriptor( + name='LanguageOption', + full_name='LanguageOption', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='LANGOPTION_NONE', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='LANGOPTION_TEXT', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='LANGOPTION_SPEECH', index=2, number=2, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='LANGOPTION_TEXT_AND_SPEECH', index=3, number=3, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=2142, + serialized_end=2255, +) +_sym_db.RegisterEnumDescriptor(_LANGUAGEOPTION) + +LanguageOption = enum_type_wrapper.EnumTypeWrapper(_LANGUAGEOPTION) +_LANGUAGESETTINGTYPE = _descriptor.EnumDescriptor( + name='LanguageSettingType', + full_name='LanguageSettingType', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='LANGSETTING_NONE', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='LANGSETTING_SINGLE', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='LANGSETTING_SIMPLE', index=2, number=2, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='LANGSETTING_ADVANCED', index=3, number=3, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=2257, + serialized_end=2374, +) +_sym_db.RegisterEnumDescriptor(_LANGUAGESETTINGTYPE) + +LanguageSettingType = enum_type_wrapper.EnumTypeWrapper(_LANGUAGESETTINGTYPE) +_SHORTCUTOPTION = _descriptor.EnumDescriptor( + name='ShortcutOption', + full_name='ShortcutOption', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='SHORTCUT_NONE', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='SHORTCUT_USER', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='SHORTCUT_ALL_USERS', index=2, number=2, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=2376, + serialized_end=2454, +) +_sym_db.RegisterEnumDescriptor(_SHORTCUTOPTION) + +ShortcutOption = enum_type_wrapper.EnumTypeWrapper(_SHORTCUTOPTION) +_OPERATION = _descriptor.EnumDescriptor( + name='Operation', + full_name='Operation', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='OP_UPDATE', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='OP_BACKFILL', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='OP_REPAIR', index=2, number=2, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='OP_NONE', index=3, number=-1, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=2456, + serialized_end=2536, +) +_sym_db.RegisterEnumDescriptor(_OPERATION) + +Operation = enum_type_wrapper.EnumTypeWrapper(_OPERATION) +LANGOPTION_NONE = 0 +LANGOPTION_TEXT = 1 +LANGOPTION_SPEECH = 2 +LANGOPTION_TEXT_AND_SPEECH = 3 +LANGSETTING_NONE = 0 +LANGSETTING_SINGLE = 1 +LANGSETTING_SIMPLE = 2 +LANGSETTING_ADVANCED = 3 +SHORTCUT_NONE = 0 +SHORTCUT_USER = 1 +SHORTCUT_ALL_USERS = 2 +OP_UPDATE = 0 +OP_BACKFILL = 1 +OP_REPAIR = 2 +OP_NONE = -1 + + +_LANGUAGESETTING = _descriptor.Descriptor( + name='LanguageSetting', + full_name='LanguageSetting', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='language', full_name='LanguageSetting.language', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='option', full_name='LanguageSetting.option', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=20, + serialized_end=88, +) + + +_USERSETTINGS = _descriptor.Descriptor( + name='UserSettings', + full_name='UserSettings', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='install_path', full_name='UserSettings.install_path', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='play_region', full_name='UserSettings.play_region', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='desktop_shortcut', full_name='UserSettings.desktop_shortcut', index=2, + number=3, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='startmenu_shortcut', full_name='UserSettings.startmenu_shortcut', index=3, + number=4, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='language_settings', full_name='UserSettings.language_settings', index=4, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='selected_text_language', full_name='UserSettings.selected_text_language', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='selected_speech_language', full_name='UserSettings.selected_speech_language', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='languages', full_name='UserSettings.languages', index=7, + number=8, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='gfx_override_tags', full_name='UserSettings.gfx_override_tags', index=8, + number=9, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='versionbranch', full_name='UserSettings.versionbranch', index=9, + number=10, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=91, + serialized_end=438, +) + + +_INSTALLHANDSHAKE = _descriptor.Descriptor( + name='InstallHandshake', + full_name='InstallHandshake', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='product', full_name='InstallHandshake.product', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='uid', full_name='InstallHandshake.uid', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='settings', full_name='InstallHandshake.settings', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=440, + serialized_end=521, +) + + +_BUILDCONFIG = _descriptor.Descriptor( + name='BuildConfig', + full_name='BuildConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='region', full_name='BuildConfig.region', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='build_config', full_name='BuildConfig.build_config', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=523, + serialized_end=574, +) + + +_BASEPRODUCTSTATE = _descriptor.Descriptor( + name='BaseProductState', + full_name='BaseProductState', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='installed', full_name='BaseProductState.installed', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='playable', full_name='BaseProductState.playable', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='update_complete', full_name='BaseProductState.update_complete', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='background_download_available', full_name='BaseProductState.background_download_available', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='background_download_complete', full_name='BaseProductState.background_download_complete', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='current_version', full_name='BaseProductState.current_version', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='current_version_str', full_name='BaseProductState.current_version_str', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='installed_build_config', full_name='BaseProductState.installed_build_config', index=7, + number=8, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='background_download_build_config', full_name='BaseProductState.background_download_build_config', index=8, # noqa + number=9, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='decryption_key', full_name='BaseProductState.decryption_key', index=9, + number=10, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='completed_install_actions', full_name='BaseProductState.completed_install_actions', index=10, + number=11, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=577, + serialized_end=949, +) + + +_BACKFILLPROGRESS = _descriptor.Descriptor( + name='BackfillProgress', + full_name='BackfillProgress', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='progress', full_name='BackfillProgress.progress', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='backgrounddownload', full_name='BackfillProgress.backgrounddownload', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='paused', full_name='BackfillProgress.paused', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='download_limit', full_name='BackfillProgress.download_limit', index=3, + number=4, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=951, + serialized_end=1055, +) + + +_REPAIRPROGRESS = _descriptor.Descriptor( + name='RepairProgress', + full_name='RepairProgress', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='progress', full_name='RepairProgress.progress', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1057, + serialized_end=1091, +) + + +_UPDATEPROGRESS = _descriptor.Descriptor( + name='UpdateProgress', + full_name='UpdateProgress', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='last_disc_set_used', full_name='UpdateProgress.last_disc_set_used', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='progress', full_name='UpdateProgress.progress', index=1, + number=2, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='disc_ignored', full_name='UpdateProgress.disc_ignored', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='total_to_download', full_name='UpdateProgress.total_to_download', index=3, + number=4, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='download_remaining', full_name='UpdateProgress.download_remaining', index=4, + number=5, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1094, + serialized_end=1233, +) + + +_CACHEDPRODUCTSTATE = _descriptor.Descriptor( + name='CachedProductState', + full_name='CachedProductState', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='base_product_state', full_name='CachedProductState.base_product_state', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='backfill_progress', full_name='CachedProductState.backfill_progress', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='repair_progress', full_name='CachedProductState.repair_progress', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='update_progress', full_name='CachedProductState.update_progress', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1236, + serialized_end=1433, +) + + +_PRODUCTOPERATIONS = _descriptor.Descriptor( + name='ProductOperations', + full_name='ProductOperations', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='active_operation', full_name='ProductOperations.active_operation', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='priority', full_name='ProductOperations.priority', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1435, + serialized_end=1510, +) + + +_PRODUCTINSTALL = _descriptor.Descriptor( + name='ProductInstall', + full_name='ProductInstall', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='uid', full_name='ProductInstall.uid', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='product_code', full_name='ProductInstall.product_code', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='settings', full_name='ProductInstall.settings', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='cached_product_state', full_name='ProductInstall.cached_product_state', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='product_operations', full_name='ProductInstall.product_operations', index=4, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1513, + serialized_end=1696, +) + + +_PRODUCTCONFIG = _descriptor.Descriptor( + name='ProductConfig', + full_name='ProductConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='product_code', full_name='ProductConfig.product_code', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='metadata_hash', full_name='ProductConfig.metadata_hash', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='timestamp', full_name='ProductConfig.timestamp', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1698, + serialized_end=1777, +) + + +_ACTIVEPROCESS = _descriptor.Descriptor( + name='ActiveProcess', + full_name='ActiveProcess', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='process_name', full_name='ActiveProcess.process_name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='pid', full_name='ActiveProcess.pid', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='uri', full_name='ActiveProcess.uri', index=2, + number=3, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1779, + serialized_end=1842, +) + + +_DOWNLOADSETTINGS = _descriptor.Descriptor( + name='DownloadSettings', + full_name='DownloadSettings', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='download_limit', full_name='DownloadSettings.download_limit', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='backfill_limit', full_name='DownloadSettings.backfill_limit', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1844, + serialized_end=1910, +) + + +_PRODUCTDB = _descriptor.Descriptor( + name='ProductDb', + full_name='ProductDb', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='product_installs', full_name='ProductDb.product_installs', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='active_installs', full_name='ProductDb.active_installs', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='active_processes', full_name='ProductDb.active_processes', index=2, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='product_configs', full_name='ProductDb.product_configs', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='download_settings', full_name='ProductDb.download_settings', index=4, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1913, + serialized_end=2140, +) + +_LANGUAGESETTING.fields_by_name['option'].enum_type = _LANGUAGEOPTION +_USERSETTINGS.fields_by_name['desktop_shortcut'].enum_type = _SHORTCUTOPTION +_USERSETTINGS.fields_by_name['startmenu_shortcut'].enum_type = _SHORTCUTOPTION +_USERSETTINGS.fields_by_name['language_settings'].enum_type = _LANGUAGESETTINGTYPE +_USERSETTINGS.fields_by_name['languages'].message_type = _LANGUAGESETTING +_INSTALLHANDSHAKE.fields_by_name['settings'].message_type = _USERSETTINGS +_BASEPRODUCTSTATE.fields_by_name['installed_build_config'].message_type = _BUILDCONFIG +_BASEPRODUCTSTATE.fields_by_name['background_download_build_config'].message_type = _BUILDCONFIG +_CACHEDPRODUCTSTATE.fields_by_name['base_product_state'].message_type = _BASEPRODUCTSTATE +_CACHEDPRODUCTSTATE.fields_by_name['backfill_progress'].message_type = _BACKFILLPROGRESS +_CACHEDPRODUCTSTATE.fields_by_name['repair_progress'].message_type = _REPAIRPROGRESS +_CACHEDPRODUCTSTATE.fields_by_name['update_progress'].message_type = _UPDATEPROGRESS +_PRODUCTOPERATIONS.fields_by_name['active_operation'].enum_type = _OPERATION +_PRODUCTINSTALL.fields_by_name['settings'].message_type = _USERSETTINGS +_PRODUCTINSTALL.fields_by_name['cached_product_state'].message_type = _CACHEDPRODUCTSTATE +_PRODUCTINSTALL.fields_by_name['product_operations'].message_type = _PRODUCTOPERATIONS +_PRODUCTDB.fields_by_name['product_installs'].message_type = _PRODUCTINSTALL +_PRODUCTDB.fields_by_name['active_installs'].message_type = _INSTALLHANDSHAKE +_PRODUCTDB.fields_by_name['active_processes'].message_type = _ACTIVEPROCESS +_PRODUCTDB.fields_by_name['product_configs'].message_type = _PRODUCTCONFIG +_PRODUCTDB.fields_by_name['download_settings'].message_type = _DOWNLOADSETTINGS +DESCRIPTOR.message_types_by_name['LanguageSetting'] = _LANGUAGESETTING +DESCRIPTOR.message_types_by_name['UserSettings'] = _USERSETTINGS +DESCRIPTOR.message_types_by_name['InstallHandshake'] = _INSTALLHANDSHAKE +DESCRIPTOR.message_types_by_name['BuildConfig'] = _BUILDCONFIG +DESCRIPTOR.message_types_by_name['BaseProductState'] = _BASEPRODUCTSTATE +DESCRIPTOR.message_types_by_name['BackfillProgress'] = _BACKFILLPROGRESS +DESCRIPTOR.message_types_by_name['RepairProgress'] = _REPAIRPROGRESS +DESCRIPTOR.message_types_by_name['UpdateProgress'] = _UPDATEPROGRESS +DESCRIPTOR.message_types_by_name['CachedProductState'] = _CACHEDPRODUCTSTATE +DESCRIPTOR.message_types_by_name['ProductOperations'] = _PRODUCTOPERATIONS +DESCRIPTOR.message_types_by_name['ProductInstall'] = _PRODUCTINSTALL +DESCRIPTOR.message_types_by_name['ProductConfig'] = _PRODUCTCONFIG +DESCRIPTOR.message_types_by_name['ActiveProcess'] = _ACTIVEPROCESS +DESCRIPTOR.message_types_by_name['DownloadSettings'] = _DOWNLOADSETTINGS +DESCRIPTOR.message_types_by_name['ProductDb'] = _PRODUCTDB +DESCRIPTOR.enum_types_by_name['LanguageOption'] = _LANGUAGEOPTION +DESCRIPTOR.enum_types_by_name['LanguageSettingType'] = _LANGUAGESETTINGTYPE +DESCRIPTOR.enum_types_by_name['ShortcutOption'] = _SHORTCUTOPTION +DESCRIPTOR.enum_types_by_name['Operation'] = _OPERATION + +LanguageSetting = _reflection.GeneratedProtocolMessageType('LanguageSetting', (_message.Message,), dict( + DESCRIPTOR=_LANGUAGESETTING, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:LanguageSetting) +)) +_sym_db.RegisterMessage(LanguageSetting) + +UserSettings = _reflection.GeneratedProtocolMessageType('UserSettings', (_message.Message,), dict( + DESCRIPTOR=_USERSETTINGS, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:UserSettings) +)) +_sym_db.RegisterMessage(UserSettings) + +InstallHandshake = _reflection.GeneratedProtocolMessageType('InstallHandshake', (_message.Message,), dict( + DESCRIPTOR=_INSTALLHANDSHAKE, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:InstallHandshake) +)) +_sym_db.RegisterMessage(InstallHandshake) + +BuildConfig = _reflection.GeneratedProtocolMessageType('BuildConfig', (_message.Message,), dict( + DESCRIPTOR=_BUILDCONFIG, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:BuildConfig) +)) +_sym_db.RegisterMessage(BuildConfig) + +BaseProductState = _reflection.GeneratedProtocolMessageType('BaseProductState', (_message.Message,), dict( + DESCRIPTOR=_BASEPRODUCTSTATE, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:BaseProductState) +)) +_sym_db.RegisterMessage(BaseProductState) + +BackfillProgress = _reflection.GeneratedProtocolMessageType('BackfillProgress', (_message.Message,), dict( + DESCRIPTOR=_BACKFILLPROGRESS, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:BackfillProgress) +)) +_sym_db.RegisterMessage(BackfillProgress) + +RepairProgress = _reflection.GeneratedProtocolMessageType('RepairProgress', (_message.Message,), dict( + DESCRIPTOR=_REPAIRPROGRESS, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:RepairProgress) +)) +_sym_db.RegisterMessage(RepairProgress) + +UpdateProgress = _reflection.GeneratedProtocolMessageType('UpdateProgress', (_message.Message,), dict( + DESCRIPTOR=_UPDATEPROGRESS, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:UpdateProgress) +)) +_sym_db.RegisterMessage(UpdateProgress) + +CachedProductState = _reflection.GeneratedProtocolMessageType('CachedProductState', (_message.Message,), dict( + DESCRIPTOR=_CACHEDPRODUCTSTATE, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:CachedProductState) +)) +_sym_db.RegisterMessage(CachedProductState) + +ProductOperations = _reflection.GeneratedProtocolMessageType('ProductOperations', (_message.Message,), dict( + DESCRIPTOR=_PRODUCTOPERATIONS, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:ProductOperations) +)) +_sym_db.RegisterMessage(ProductOperations) + +ProductInstall = _reflection.GeneratedProtocolMessageType('ProductInstall', (_message.Message,), dict( + DESCRIPTOR=_PRODUCTINSTALL, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:ProductInstall) +)) +_sym_db.RegisterMessage(ProductInstall) + +ProductConfig = _reflection.GeneratedProtocolMessageType('ProductConfig', (_message.Message,), dict( + DESCRIPTOR=_PRODUCTCONFIG, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:ProductConfig) +)) +_sym_db.RegisterMessage(ProductConfig) + +ActiveProcess = _reflection.GeneratedProtocolMessageType('ActiveProcess', (_message.Message,), dict( + DESCRIPTOR=_ACTIVEPROCESS, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:ActiveProcess) +)) +_sym_db.RegisterMessage(ActiveProcess) + +DownloadSettings = _reflection.GeneratedProtocolMessageType('DownloadSettings', (_message.Message,), dict( + DESCRIPTOR=_DOWNLOADSETTINGS, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:DownloadSettings) +)) +_sym_db.RegisterMessage(DownloadSettings) + +ProductDb = _reflection.GeneratedProtocolMessageType('ProductDb', (_message.Message,), dict( + DESCRIPTOR=_PRODUCTDB, + __module__='product_db_pb2' + # @@protoc_insertion_point(class_scope:ProductDb) +)) +_sym_db.RegisterMessage(ProductDb) + + +# @@protoc_insertion_point(module_scope) diff --git a/setup.py b/setup.py index bfb2941a7..df249e04f 100644 --- a/setup.py +++ b/setup.py @@ -40,6 +40,7 @@ setup( 'lutris.services', 'lutris.util', 'lutris.util.amazon', + 'lutris.util.battlenet', 'lutris.util.discord', 'lutris.util.dolphin', 'lutris.util.egs', @@ -65,6 +66,7 @@ setup( 'pypresence', 'PyYAML', 'requests', + 'protobuf', 'moddb >= 0.8.1' ], url='https://lutris.net',