async def get_local_games(self): # Since the API requires that get_local_games returns a list of LocalGame objects, local_list is the value that # needs to be returned. However, for internal use (the self.local_games_cache field), the dictionary local_games # is used for greater flexibility. local_games = {} local_list = [] for game in self.total_games_cache: title_id = get_game_title_id_from_ros_title_id(str(game.game_id)) check = self._local_client.get_path_to_game(title_id) if check is not None: if (title_id in self.running_games_pids and check_if_process_exists( self.running_games_pids[title_id][0])): local_game = self.create_local_game_from_title_id( title_id, True, True) else: local_game = self.create_local_game_from_title_id( title_id, False, True) else: local_game = self.create_local_game_from_title_id( title_id, False, False) local_games[title_id] = local_game local_list.append(local_game) self.local_games_cache = local_games log.debug("ROCKSTAR_INSTALLED_GAMES: " + str(local_games)) return local_list
async def get_unlocked_achievements(self, game_id, context): # The Social Club API has an authentication endpoint located at https://scapi.rockstargames.com/ # achievements/awardedAchievements?title=[game-id]&platform=pc&rockstarId=[rockstar-ID], which returns a # list of the user's unlocked achievements for the specified game. It uses the Social Club standard for # authentication (a request header named Authorization containing "Bearer [Bearer-Token]"). title_id = get_game_title_id_from_ros_title_id(game_id) if games_cache[title_id]["achievementId"] is None or \ (games_cache[title_id]["isPreOrder"]): return [] log.debug("ROCKSTAR_ACHIEVEMENT_CHECK: Beginning achievements check for " + title_id + " (Achievement ID: " + get_achievement_id_from_ros_title_id(game_id) + ")...") # Now, we can begin getting the user's achievements for the specified game. achievement_id = get_achievement_id_from_ros_title_id(game_id) url = (f"https://scapi.rockstargames.com/achievements/awardedAchievements?title={achievement_id}" f"&platform=pc&rockstarId={self._http_client.get_rockstar_id()}") unlocked_achievements = await self._http_client.get_json_from_request_strict(url) achievements_dict = unlocked_achievements["awardedAchievements"] achievements_list = [] for key, value in achievements_dict.items(): # What if an achievement is added to the Social Club after the cache was already made? In this event, we # need to refresh the cache. achievement_num = key unlock_time = await get_unix_epoch_time_from_date(value["dateAchieved"]) achievements_list.append(Achievement(unlock_time, achievement_id=achievement_num)) return achievements_list
async def uninstall_game(self, game_id): if not self._local_client.get_local_launcher_path(): await self.open_rockstar_browser() return title_id = get_game_title_id_from_ros_title_id(game_id) log.debug("ROCKSTAR_UNINSTALL_REQUEST: Requesting to uninstall " + title_id + "...") self._local_client.uninstall_game_from_title_id(title_id)
async def launch_game(self, game_id): title_id = get_game_title_id_from_ros_title_id(game_id) self.running_games_pids[title_id] = [ await self._local_client.launch_game_from_title_id(title_id), True ] log.debug("ROCKSTAR_PIDS: " + str(self.running_games_pids)) if self.running_games_pids[title_id][0] != '-1': self.update_local_game_status( LocalGame(game_id, LocalGameState.Running | LocalGameState.Installed))
async def install_game(self, game_id): if not self._local_client.get_local_launcher_path(): await self.open_rockstar_browser() return title_id = get_game_title_id_from_ros_title_id(game_id) log.debug("ROCKSTAR_INSTALL_REQUEST: Requesting to install " + title_id + "...") # There is no need to check if the game is a pre-order, since the InstallLocation registry key will be # unavailable if it is. self._local_client.install_game_from_title_id(title_id)
async def install_game(self, game_id): title_id = get_game_title_id_from_ros_title_id(game_id) log.debug("ROCKSTAR_INSTALL_REQUEST: Requesting to install " + title_id + "...") self._local_client.install_game_from_title_id(title_id) # If the game is not released yet, then we should allow them to see this on the Rockstar Games Launcher, but the # game's installation status should not be changed. if games_cache[title_id]["isPreOrder"]: return self.update_local_game_status( LocalGame(game_id, LocalGameState.Installed))
async def get_local_games(self): # Since the API requires that get_local_games returns a list of LocalGame objects, local_list is the value # that needs to be returned. However, for internal use (the self.local_games_cache field), the dictionary # local_games is used for greater flexibility. local_games = {} local_list = [] for game in self.total_games_cache: title_id = get_game_title_id_from_ros_title_id(str(game.game_id)) local_game = self.check_game_status(title_id) local_games[title_id] = local_game local_list.append(local_game) self.local_games_cache = local_games log.debug(f"ROCKSTAR_INSTALLED_GAMES: {local_games}") return local_list
async def launch_game(self, game_id): if not self._local_client.get_local_launcher_path(): await self.open_rockstar_browser() return title_id = get_game_title_id_from_ros_title_id(game_id) game_pid = await self._local_client.launch_game_from_title_id(title_id) if game_pid: self.running_games_info_list[title_id] = RunningGameInfo() self.running_games_info_list[title_id].set_info(game_pid) log.debug(f"ROCKSTAR_PIDS: {self.list_running_game_pids()}") local_game = LocalGame(game_id, LocalGameState.Running | LocalGameState.Installed) self.update_local_game_status(local_game) self.local_games_cache[title_id] = local_game else: log.error(f'cannot start game: {title_id}')
async def get_game_time(self, game_id, context): # Although the Rockstar Games Launcher does track the played time for each game, there is currently no known # method for accessing this information. As such, game time will be recorded when games are launched through the # Galaxy 2.0 client. title_id = get_game_title_id_from_ros_title_id(game_id) if title_id in self.running_games_info_list: # The game is running (or has been running). start_time = self.running_games_info_list[title_id].get_start_time( ) self.running_games_info_list[title_id].update_start_time() current_time = datetime.datetime.now().timestamp() minutes_passed = (current_time - start_time) / 60 if not self.running_games_info_list[title_id].get_pid(): # The PID has been set to None, which means that the game has exited (see self.check_game_status). Now # that the start time is recorded, the game can be safely removed from the list of running games. del self.running_games_info_list[title_id] if self.game_time_cache[title_id]['time_played']: # The game has been played before, so the time will need to be added to the existing cached time. total_time_played = self.game_time_cache[title_id][ 'time_played'] + minutes_passed self.game_time_cache[title_id][ 'time_played'] = total_time_played self.game_time_cache[title_id]['last_played'] = current_time return GameTime(game_id=game_id, time_played=int(total_time_played), last_played_time=int(current_time)) else: # The game has not been played before, so a new entry in the game_time_cache dictionary must be made. self.game_time_cache[title_id] = { 'time_played': minutes_passed, 'last_played': current_time } return GameTime(game_id=game_id, time_played=int(minutes_passed), last_played_time=int(current_time)) else: # The game is no longer running (and there is no relevant entry in self.running_games_info_list). if title_id not in self.game_time_cache: self.game_time_cache[title_id] = { 'time_played': None, 'last_played': None } return GameTime( game_id=game_id, time_played=self.game_time_cache[title_id]['time_played'], last_played_time=self.game_time_cache[title_id]['last_played'])
async def get_local_games(self): local_games = [] for game in self.total_games_cache: title_id = get_game_title_id_from_ros_title_id(str(game.game_id)) check = self._local_client.get_path_to_game(title_id) if check is not None: if (title_id in self.running_games_pids and check_if_process_exists(self.running_games_pids[title_id][0])): local_game = self.create_local_game_from_title_id(title_id, True, True) else: local_game = self.create_local_game_from_title_id(title_id, False, True) local_games.append(local_game) else: local_games.append(self.create_local_game_from_title_id(title_id, False, False)) self.local_games_cache = local_games log.debug("ROCKSTAR_INSTALLED_GAMES: " + str(local_games)) return local_games
async def get_local_games(self): # Since the API requires that get_local_games returns a list of LocalGame objects, local_list is the value # that needs to be returned. However, for internal use (the self.local_games_cache field), the dictionary # local_games is used for greater flexibility. local_games = {} local_list = [] state = LocalGameState.None_ for game in self.total_games_cache: title_id = get_game_title_id_from_ros_title_id( str(game.game_id)) game_installed = self._local_client.get_path_to_game(title_id) if title_id != "launcher" and game_installed: state |= LocalGameState.Installed local_game = self.check_game_status(title_id) local_games[title_id] = local_game local_list.append(local_game) else: continue self.local_games_cache = local_games log.debug(f"ROCKSTAR_INSTALLED_GAMES: {local_games}") return local_list
async def uninstall_game(self, game_id): title_id = get_game_title_id_from_ros_title_id(game_id) log.debug("ROCKSTAR_UNINSTALL_REQUEST: Requesting to uninstall " + title_id + "...") self._local_client.uninstall_game_from_title_id(title_id) self.update_local_game_status(LocalGame(game_id, LocalGameState.None_))
async def get_owned_games(self): # Here is the actual implementation of getting the user's owned games: # -Get the list of games_played from rockstargames.com/auth/get-user.json. # -If possible, use the launcher log to confirm which games are actual launcher games and which are # Steam/Retail games. # -If it is not possible to use the launcher log, then just use the list provided by the website. if not self.is_authenticated(): raise AuthenticationRequired() # Get the list of games_played from https://www.rockstargames.com/auth/get-user.json. owned_title_ids = [] online_check_success = True try: played_games = await self._http_client.get_played_games() for game in played_games: title_id = get_game_title_id_from_online_title_id(game) owned_title_ids.append(title_id) log.debug("ROCKSTAR_ONLINE_GAME: Found played game " + title_id + "!") except Exception as e: log.error( "ROCKSTAR_PLAYED_GAMES_ERROR: The exception " + repr(e) + " was thrown when attempting to get the" " user's played games online. Falling back to log file check..." ) online_check_success = False # The log is in the Documents folder. current_log_count = 0 log_file = None log_file_append = "" # The Rockstar Games Launcher generates 10 log files before deleting them in a FIFO fashion. Old log files are # given a number ranging from 1 to 9 in their name. In case the first log file does not have all of the games, # we need to check the other log files, if possible. while current_log_count < 10: try: if current_log_count != 0: log_file_append = ".0" + str(current_log_count) log_file = os.path.join( self.documents_location, "Rockstar Games\\Launcher\\launcher" + log_file_append + ".log") log.debug("ROCKSTAR_LOG_LOCATION: Checking the file " + log_file + "...") owned_title_ids = await self.parse_log_file( log_file, owned_title_ids, online_check_success) break except NoGamesInLogException: log.warning( "ROCKSTAR_LOG_WARNING: There are no owned games listed in " + str(log_file) + ". Moving to " "the next log file...") current_log_count += 1 except NoLogFoundException: log.warning( "ROCKSTAR_LAST_LOG_REACHED: There are no more log files that can be found and/or read " "from. Assuming that the online list is correct...") break except Exception: # This occurs after ROCKSTAR_LOG_ERROR. break if current_log_count == 10: log.warning( "ROCKSTAR_LAST_LOG_REACHED: There are no more log files that can be found and/or read " "from. Assuming that the online list is correct...") remove_all = False if len(self.owned_games_cache) == 0: remove_all = True for title_id in owned_title_ids: game = self.create_game_from_title_id(title_id) if game not in self.owned_games_cache: log.debug("ROCKSTAR_ADD_GAME: Adding " + title_id + " to owned games cache...") self.add_game(game) self.owned_games_cache.append(game) if remove_all is True: for key, value in games_cache.items(): if key not in owned_title_ids: log.debug("ROCKSTAR_REMOVE_GAME: Removing " + key + " from owned games cache...") self.remove_game(value['rosTitleId']) else: for game in self.owned_games_cache: if get_game_title_id_from_ros_title_id( game.game_id) not in owned_title_ids: log.debug( "ROCKSTAR_REMOVE_GAME: Removing " + get_game_title_id_from_ros_title_id(game.game_id) + " from owned games cache...") self.remove_game(game.game_id) self.owned_games_cache.remove(game) return self.owned_games_cache
async def get_local_size(self, game_id: str, context: Any) -> Optional[int]: title_id = get_game_title_id_from_ros_title_id(game_id) game_status = self.check_game_status(title_id) if game_status.local_game_state == LocalGameState.None_: return 0 return await self._local_client.get_game_size_in_bytes(title_id)
async def get_local_size(self, game_id: str, context: Any) -> Optional[int]: title_id = get_game_title_id_from_ros_title_id(game_id) return await self._local_client.get_game_size_in_bytes(title_id)