diff --git a/misc/requirements.in b/misc/requirements.in index bc5c91944..8367ea3de 100644 --- a/misc/requirements.in +++ b/misc/requirements.in @@ -4,7 +4,7 @@ setuptools-scm requests < 3.0 PySide6-Essentials >= 6.8.1 QtAwesome -legendary-gl @ https://github.com/RareDevs/legendary/archive/rare-next.zip +legendary-gl @ https://github.com/RareDevs/legendary/archive/207b859ae4476ae77466d3c922efaba90e093f12.zip orjson vdf @ https://github.com/solsticegamestudios/vdf/archive/be1f7220238022f8b29fe747f0b643f280bfdb6e.zip pywin32 ; platform_system == "Windows" diff --git a/pyproject.toml b/pyproject.toml index 9d3688a3d..a4810106a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ dependencies = [ "requests < 3.0", "PySide6-Essentials >= 6.8.1", "QtAwesome", - "legendary-gl @ git+https://github.com/RareDevs/legendary@rare-next", + "legendary-gl @ git+https://github.com/RareDevs/legendary@207b859ae4476ae77466d3c922efaba90e093f12", "orjson", "vdf @ https://github.com/solsticegamestudios/vdf/archive/be1f7220238022f8b29fe747f0b643f280bfdb6e.zip", "pywin32 ; platform_system == 'Windows'", diff --git a/rare/commands/launcher/__init__.py b/rare/commands/launcher/__init__.py index a87a194f1..c35e20927 100644 --- a/rare/commands/launcher/__init__.py +++ b/rare/commands/launcher/__init__.py @@ -335,7 +335,7 @@ def launch_game(self, params: LaunchParams): self.stop() return - if platform.system() == 'Windows' and params.is_origin_game: + if platform.system() == 'Windows' and params.is_third_party_game: # executable is a protocol link (link2ea://launchgame/...) QDesktopServices.openUrl(QUrl(params.executable)) self.stop() # stop because it is not a subprocess diff --git a/rare/commands/launcher/lgd_helper.py b/rare/commands/launcher/lgd_helper.py index 4b5bc39f3..428dccff1 100644 --- a/rare/commands/launcher/lgd_helper.py +++ b/rare/commands/launcher/lgd_helper.py @@ -52,28 +52,32 @@ class LaunchParams: environment: dict[str, str] = field(default_factory=dict) pre_launch_command: str = '' pre_launch_wait: bool = False - is_origin_game: bool = False # only for windows to launch as url + is_third_party_game: bool = False # only for windows to launch as url def __bool__(self): return bool(self.executable) -def get_origin_params(rgame: RareGameSlim, init: InitParams, launch: LaunchParams) -> LaunchParams: +def get_third_party_params(rgame: RareGameSlim, init: InitParams, launch: LaunchParams) -> LaunchParams: core = rgame.core app_name = rgame.app_name - origin_uri = core.get_origin_uri(app_name, init.offline) + uri = ( + core.get_origin_uri(app_name, init.offline) + if rgame.is_origin + else core.get_ubisoft_uri(app_name, init.offline) + ) if platform.system() == 'Windows': - command = [origin_uri] + command = [uri] else: command = core.get_app_launch_command(app_name) if not os.path.exists(command[0]) and shutil.which(command[0]) is None: return launch - command.append(origin_uri) + command.append(uri) exe, args, env = prepare_process(command, core.get_app_environment(app_name)) - launch.is_origin_game = True + launch.is_third_party_game = True launch.executable = exe launch.arguments = args launch.environment = env @@ -132,7 +136,7 @@ def get_launch_params(rgame: RareGameSlim, init: InitParams = None) -> LaunchPar if not rgame.game: raise GameArgsError(f'Could not find metadata for {rgame.app_title}') - if rgame.is_origin: + if rgame.is_third_party: init.offline = False else: if not rgame.is_installed: @@ -143,8 +147,8 @@ def get_launch_params(rgame: RareGameSlim, init: InitParams = None) -> LaunchPar if not os.path.exists(rgame.install_path): raise GameArgsError('Game path does not exist') - if rgame.is_origin: - resp = get_origin_params(rgame, init, resp) + if rgame.is_third_party: + resp = get_third_party_params(rgame, init, resp) else: resp = get_game_params(rgame, init, resp) diff --git a/rare/components/tabs/library/details/__init__.py b/rare/components/tabs/library/details/__init__.py index 2cef232d1..dce33cfc7 100644 --- a/rare/components/tabs/library/details/__init__.py +++ b/rare/components/tabs/library/details/__init__.py @@ -65,14 +65,14 @@ def update_game(self, rgame: RareGame): self.details_tab.update_game(rgame) self.game_settings_tab.load_settings(rgame) - self.game_settings_tab.launch.setEnabled(rgame.is_installed or rgame.is_origin) + self.game_settings_tab.launch.setEnabled(rgame.is_installed or rgame.is_third_party) if pf.system() != 'Windows': self.compat_settings_tab.load_settings(rgame) - self.compat_settings_tab.setEnabled(rgame.is_installed or rgame.is_origin) + self.compat_settings_tab.setEnabled(rgame.is_installed or rgame.is_third_party) self.environ_tab.load_settings(rgame) - self.environ_tab.setEnabled(rgame.is_installed or rgame.is_origin) + self.environ_tab.setEnabled(rgame.is_installed or rgame.is_third_party) self.dlcs_tab.update_dlcs(rgame) self.dlcs_tab.setEnabled(rgame.is_installed and bool(rgame.owned_dlcs)) diff --git a/rare/components/tabs/library/details/compat.py b/rare/components/tabs/library/details/compat.py index 9a6012ca9..cc4670301 100644 --- a/rare/components/tabs/library/details/compat.py +++ b/rare/components/tabs/library/details/compat.py @@ -44,7 +44,11 @@ def load_settings(self, app_name: str): def _get_compat_path(self, compat_location: ProtonSettings.CompatLocation): folder_name = 'default' - local_folder_name = self.rcore.get_game(self.app_name).folder_name + rgame = self.rcore.get_game(self.app_name) + if rgame.is_third_party: + local_folder_name = "Origin" if rgame.is_origin else "Ubisoft" + else: + local_folder_name = rgame.folder_name if compat_location == ProtonSettings.CompatLocation.NONE: if wine_prefix_dir(local_folder_name).joinpath('system.reg').is_file(): compat_location = ProtonSettings.CompatLocation.ISOLATED diff --git a/rare/components/tabs/library/details/details.py b/rare/components/tabs/library/details/details.py index ef63856bb..babd81bb4 100644 --- a/rare/components/tabs/library/details/details.py +++ b/rare/components/tabs/library/details/details.py @@ -432,8 +432,10 @@ def update_game(self, rgame: RareGame): self.ui.app_name.setText(rgame.app_name) self.ui.dev.setText(rgame.developer) - if rgame.is_non_asset: - self.ui.install_button.setText(self.tr('Link/Launch')) + if rgame.is_non_asset or rgame.is_third_party: + self.ui.install_button.setText( + self.tr('Launch in EA App') if rgame.is_origin else self.tr('Launch in Ubisoft Connect') + ) self.ui.actions_stack.setCurrentWidget(self.ui.uninstalled_page) else: self.ui.install_button.setText(self.tr('Install')) diff --git a/rare/components/tabs/library/widgets/game_widget.py b/rare/components/tabs/library/widgets/game_widget.py index 995831470..0aaa9dfc7 100644 --- a/rare/components/tabs/library/widgets/game_widget.py +++ b/rare/components/tabs/library/widgets/game_widget.py @@ -92,7 +92,8 @@ def __init__(self, rgame: RareGame, parent=None): 'can_launch': self.tr('Launch game'), 'is_foreign': self.tr('Launch offline'), 'has_update': self.tr('Launch without version check'), - 'is_origin': self.tr('Launch/Link'), + 'is_origin': self.tr('Launch in EA App'), + 'is_ubisoft': self.tr('Launch in Ubisoft Connect'), 'not_can_launch': self.tr("Can't launch"), } @@ -169,7 +170,7 @@ def update_actions(self): for action in self.actions(): self.removeAction(action) - if self.rgame.is_installed or self.rgame.is_origin: + if self.rgame.is_installed or self.rgame.is_third_party: self.addAction(self.launch_action) else: self.addAction(self.install_action) @@ -194,7 +195,7 @@ def update_actions(self): self.addAction(self.steam_shortcut_action) self.addAction(self.reload_action) - if self.rgame.is_installed and not self.rgame.is_origin: + if self.rgame.is_installed and not self.rgame.is_third_party: self.addAction(self.uninstall_action) def eventFilter(self, a0: QObject, a1: QEvent) -> bool: @@ -216,6 +217,8 @@ def eventFilter(self, a0: QObject, a1: QEvent) -> bool: self.ui.tooltip_label.setText(self.hover_strings['not_can_launch']) elif self.rgame.is_origin: self.ui.tooltip_label.setText(self.hover_strings['is_origin']) + elif self.rgame.is_ubisoft: + self.ui.tooltip_label.setText(self.hover_strings['is_ubisoft']) elif self.rgame.has_update: self.ui.tooltip_label.setText(self.hover_strings['has_update']) elif self.rgame.is_foreign and self.rgame.can_run_offline: diff --git a/rare/components/tabs/library/widgets/list_game_widget.py b/rare/components/tabs/library/widgets/list_game_widget.py index 8a197d76b..645594c22 100644 --- a/rare/components/tabs/library/widgets/list_game_widget.py +++ b/rare/components/tabs/library/widgets/list_game_widget.py @@ -38,7 +38,11 @@ def __init__(self, rgame: RareGame, parent=None): self.ui.launch_btn.setEnabled(self.rgame.can_launch) - self.ui.launch_btn.setText(self.tr('Launch') if not self.rgame.is_origin else self.tr('Link/Play')) + if not self.rgame.is_third_party: + self.ui.launch_btn.setText(self.tr('Launch')) + else: + self.ui.launch_btn.setText(self.tr('Launch in EA App') if self.rgame.is_origin else self.tr('Launch in Ubisoft Connect')) + self.ui.developer_label.setText(self.rgame.developer) # self.version_label.setVisible(self.is_installed) if self.rgame.igame: diff --git a/rare/components/tabs/settings/widgets/proton.py b/rare/components/tabs/settings/widgets/proton.py index 47410a571..3230c55e9 100644 --- a/rare/components/tabs/settings/widgets/proton.py +++ b/rare/components/tabs/settings/widgets/proton.py @@ -150,8 +150,9 @@ def _on_tool_changed(self, index: int): steam_environ = steam.get_steam_environment(steam_tool, self.compat_edit.text()) library_paths = steam_environ.get('STEAM_COMPAT_LIBRARY_PATHS', '') if self.app_name != 'default' and (install_path := self.rcore.get_game(self.app_name).install_path): + game_library = os.path.dirname(install_path) library_paths = ( - ':'.join([library_paths, os.path.dirname(install_path)]) if library_paths else os.path.dirname(install_path) + ':'.join([library_paths, game_library]) if library_paths else game_library ) # https://gitlab.steamos.cloud/steamrt/steam-runtime-tools/-/blob/main/docs/steam-compat-tool-interface.md#non-steam-games steam_environ['STEAM_COMPAT_INSTALL_PATH'] = install_path diff --git a/rare/models/game.py b/rare/models/game.py index eb2cd9ef1..f483c55b0 100644 --- a/rare/models/game.py +++ b/rare/models/game.py @@ -91,8 +91,8 @@ def __init__( game: Game, ): super(RareGame, self).__init__(settings, legendary_core, game) - self.__origin_install_path: str | None = None - self.__origin_install_size: int | None = None + self._third_party_install_path: str | None = None + self._third_party_install_size: int | None = None self.image_manager = image_manager @@ -251,15 +251,15 @@ def install_size(self) -> int: @return int The size of the installation """ - if self.is_origin: - return self.__origin_install_size if self.__origin_install_size is not None else 0 + if self.is_third_party: + return self._third_party_install_size if self._third_party_install_size is not None else 0 return self.igame.install_size if self.igame is not None else 0 @property def install_path(self) -> str | None: - if self.is_origin: + if self.is_third_party: # TODO Linux is also C:\\... - return self.__origin_install_path + return self._third_party_install_path return super(RareGame, self).install_path @install_path.setter @@ -267,8 +267,8 @@ def install_path(self, path: str) -> None: if self.igame: self.igame.install_path = path self.store_igame() - elif self.is_origin: - self.__origin_install_path = path + elif self.is_third_party: + self._third_party_install_path = path @property def remote_version(self) -> str: @@ -314,7 +314,7 @@ def is_installed(self) -> bool: @return bool If the game should be considered installed """ - return (self.igame is not None) or (self.is_origin and self.__origin_install_path is not None) + return (self.igame is not None) or self.is_ubisoft or self.is_origin def set_installed(self, installed: bool) -> None: """! @@ -450,18 +450,14 @@ def is_non_asset(self) -> bool: @return bool If the game doesn't have assets """ - # Asset infos are usually None, but there was a bug, that it was an empty GameAsset class - return not self.game.asset_infos or not next(iter(self.game.asset_infos.values())).app_name + + return not self.game.asset_infos or not next(iter(self.game.asset_infos.values())).app_name or self.is_third_party @property def is_android_only(self) -> bool: return self.is_non_asset and self.is_android - @property - def is_ubisoft(self) -> bool: - return self.game.partner_link_type == 'ubisoft' - @property def folder_name(self) -> str: return ( @@ -632,9 +628,9 @@ def tags(self, tags: tuple[str, ...]) -> None: self.logger.debug(f'Saving tags for {self.game.app_title}: {self.metadata.tags}') self.__save_metadata() - def set_origin_attributes(self, path: str, size: int = 0) -> None: - self.__origin_install_path = path - self.__origin_install_size = size + def set_third_party_attributes(self, path: str, size: int = 0) -> None: + self._third_party_install_path = path + self._third_party_install_size = size if self.install_path and self.install_size: self.signals.game.installed.emit(self.app_name) else: @@ -643,7 +639,7 @@ def set_origin_attributes(self, path: str, size: int = 0) -> None: @property def can_launch(self) -> bool: - if self.is_idle and self.is_origin: + if self.is_idle and self.is_third_party: return True if self.is_installed: if (not self.is_idle) or self.needs_verification: diff --git a/rare/models/game_slim.py b/rare/models/game_slim.py index f97b07498..f2a49fa37 100644 --- a/rare/models/game_slim.py +++ b/rare/models/game_slim.py @@ -160,16 +160,15 @@ def is_win32(self) -> bool: @property def is_origin(self) -> bool: - """! - @brief Property to report if a Game is an Origin game + return bool(self.game.is_origin_game) - Legendary and by extenstion Rare can't launch Origin games directly, - it just launches the Origin client and thus requires a bit of a special - handling to let the user know. + @property + def is_ubisoft(self) -> bool: + return bool(self.game.is_ubisoft_game) - @return bool If the game is an Origin game - """ - return self.game.third_party_store in {'Origin', 'the EA app'} + @property + def is_third_party(self) -> bool: + return self.is_origin or self.is_ubisoft @property def is_android(self) -> bool: @@ -230,7 +229,7 @@ def __init__(self, settings: RareAppSettings, legendary_core: LegendaryCore, gam @property def is_installed(self) -> bool: - if self.is_origin: + if self.is_third_party: return True return self.igame is not None diff --git a/rare/shared/rare_core.py b/rare/shared/rare_core.py index d42e02c75..b42c24517 100644 --- a/rare/shared/rare_core.py +++ b/rare/shared/rare_core.py @@ -342,7 +342,7 @@ def __add_games_and_dlcs(self, games: list[Game], dlcs_dict: dict[str, list]) -> # lk: since loading has to know about the game's state, # validate installation just by trying to add each RareGame # TODO: this should probably be moved into RareGame - if rgame.is_installed and not (rgame.is_dlc or rgame.is_non_asset): + if rgame.is_installed and not rgame.is_third_party and not (rgame.is_dlc or rgame.is_non_asset): try: self.__validate_install(rgame) except FileNotFoundError as e: @@ -435,14 +435,14 @@ def fetch_saves(self): saves_worker = QRunnable.create(self.__fetch_saves) QThreadPool.globalInstance().start(saves_worker) - def resolve_origin(self) -> None: + def resolve_third_party(self) -> None: origin_worker = OriginWineWorker(self.__core, list(self.origin_games)) QThreadPool.globalInstance().start(origin_worker) def __post_init(self) -> None: if not self.__args.offline: self.fetch_saves() - self.resolve_origin() + self.resolve_third_party() @property def game_tags(self) -> tuple[str, ...]: @@ -460,19 +460,19 @@ def games_and_dlcs(self) -> Iterator[RareGame]: @property def games(self) -> Iterator[RareGame]: - return self.__filter_games(lambda game: not game.is_dlc or game.is_launchable_addon) + return self.__filter_games(lambda rgame: not rgame.is_dlc or rgame.is_launchable_addon) @property def installed_games(self) -> Iterator[RareGame]: - return self.__filter_games(lambda game: game.is_installed and not game.is_dlc) + return self.__filter_games(lambda rgame: rgame.is_installed and not rgame.is_dlc) @property def origin_games(self) -> Iterator[RareGame]: - return self.__filter_games(lambda game: game.is_origin and not game.is_dlc) + return self.__filter_games(lambda rgame: rgame.is_origin and not rgame.is_dlc) @property def ubisoft_games(self) -> Iterator[RareGame]: - return self.__filter_games(lambda game: game.is_ubisoft and not game.is_dlc) + return self.__filter_games(lambda rgame: rgame.is_ubisoft and not rgame.is_dlc) @property def game_list(self) -> Iterator[Game]: diff --git a/rare/shared/workers/wine_resolver.py b/rare/shared/workers/wine_resolver.py index 6ffb3ddd4..81dc63279 100644 --- a/rare/shared/workers/wine_resolver.py +++ b/rare/shared/workers/wine_resolver.py @@ -180,7 +180,7 @@ def run_real(self) -> None: if install_dir: if os.path.isdir(install_dir): install_size = path_size(install_dir) - rgame.set_origin_attributes(install_dir, install_size) + rgame.set_third_party_attributes(install_dir, install_size) self.logger.info( "Origin game '%s' (%s, %s)", rgame.app_title, diff --git a/rare/utils/paths.py b/rare/utils/paths.py index 9dae704f6..ee0fd1756 100644 --- a/rare/utils/paths.py +++ b/rare/utils/paths.py @@ -350,6 +350,7 @@ def create_desktop_link(app_name: str, app_title: str = '', link_name: str = '', desktop_file.write( '[Desktop Entry]\n' f'Name={app_title}\n' + f'Comment={app_title} (Rare)\n' 'Type=Application\n' 'Categories=Game;\n' f'Icon={icon_path}\n' diff --git a/rare/widgets/rare_app.py b/rare/widgets/rare_app.py index bdbb92501..d9a917dd1 100644 --- a/rare/widgets/rare_app.py +++ b/rare/widgets/rare_app.py @@ -76,7 +76,8 @@ def __init__(self, args: Namespace, log_file: str): self.logger = logging.getLogger(type(self).__name__) self._hook = RareAppException(self) self.setQuitOnLastWindowClosed(False) - self.setAttribute(Qt.ApplicationAttribute.AA_DontUseNativeDialogs, True) + use_qt_dialogs = not os.environ.get('SNAP') + self.setAttribute(Qt.ApplicationAttribute.AA_DontUseNativeDialogs, use_qt_dialogs) self.setDesktopFileName('rare') self.setApplicationName('Rare')