async def get_mod_versions(self, mod):
        query = """
            SELECT
                `updates_{mod}_files`.`fileId` AS file_id,
                MAX(`updates_{mod}_files`.`version`) AS version
            FROM `updates_{mod}`
            INNER JOIN `updates_{mod}_files` ON `fileId` = `updates_{mod}`.`id`
            GROUP BY `updates_{mod}_files`.`fileId`
        """.format(mod=mod)
        logger.debug(f"Performing query: {query}")

        # This is a fix for FAF db updates_* tables shindig. Mod updates should
        # be kept in just a single set of tables, but instead each mod has its
        # own set - and some don't have any, like ladder1v1!
        # As a stopgap, we'll swallow all errors that happen to this query and
        # just return an empty dict. I can't be bothered to make some sort of
        # subquery to check if the table exists, or parse exception string for
        # that - we'll live with that until we make a new replay format.
        try:
            featured_mods = await self._db.execute(query)
        except BookkeepingError as e:
            if mod != "ladder1v1":
                logger.warning((f"Failed to query mod versions for {mod}: {e}"
                                f", not saving them in replay"))
            return {}

        return {str(mod['file_id']): mod['version'] for mod in featured_mods}
 def _track_connection(self, connection):
     logger.debug(f"{self} - new connection, {connection}")
     self._connections.add(connection)
     try:
         yield
     finally:
         self._connections.remove(connection)
         logger.debug(f"{self} - connection over, {connection}")
Exemple #3
0
 async def update_game_stats(self, game_id, replay_ticks):
     query = """
         UPDATE `game_stats` SET
             `game_stats`.`replay_ticks` = %s
         WHERE `game_stats`.`id` = %s
     """
     logger.debug(f"Performing query: {query}")
     await self._db.execute(query, ((replay_ticks, game_id), ))
 async def save_replay(self, game_id, stream):
     try:
         logger.debug(f"Saving replay {game_id}")
         await self._saver.save_replay(game_id, stream)
         logger.debug(f"Saved replay {game_id}")
         metrics.saved_replays.inc()
     except BookkeepingError as e:
         logger.warn(f"Failed to save replay for game {game_id}: {e}")
Exemple #5
0
 async def _lifetime(self):
     await self.merger.wait_for_ended()
     logger.debug(f"{self} write phase ended")
     await self.bookkeeper.save_replay(self._game_id,
                                       self.merger.canonical_stream)
     await self.sender.wait_for_ended()
     self._force_close.cancel()
     self._ended.set()
     logger.debug(f"Lifetime of {self} ended")
Exemple #6
0
 async def handle_connection(self, header, connection):
     with self._track_connection(connection):
         logger.debug(f"{self} - new connection, {header}")
         if header.type == ConnectionHeader.Type.WRITER:
             await self.merger.handle_connection(connection)
         elif header.type == ConnectionHeader.Type.READER:
             await self.sender.handle_connection(connection)
         else:
             raise MalformedDataError("Invalid connection type")
         logger.debug(f"{self} - connection over, {header}")
 async def _lifetime(self):
     await self.merger.wait_for_ended()
     logger.debug(f"{self} write phase ended")
     await self.bookkeeper.save_replay(self._game_id,
                                       self.merger.canonical_stream)
     await self.sender.wait_for_ended()
     self.merger.canonical_stream.discard_all()
     for coro in self._lifetime_coroutines:
         coro.cancel()
     self._ended.set()
     logger.debug(f"Lifetime of {self} ended")
 async def get_teams_in_game(self, game_id):
     query = """
         SELECT
             `login`.`login` AS login,
             `game_player_stats`.`team` AS team
         FROM `game_stats`
         INNER JOIN `game_player_stats`
           ON `game_player_stats`.`gameId` = `game_stats`.`id`
         INNER JOIN `login`
           ON `login`.id = `game_player_stats`.`playerId`
         WHERE `game_stats`.`id` = %s AND `game_player_stats`.`AI` = 0
     """
     logger.debug(f"Performing query: {query}")
     players = await self._db.execute(query, (game_id, ))
     if not players:
         logger.warning(
             f"No players found for game {game_id}, will try to save anyway."
         )
     teams = {}
     for player in players:
         teams.setdefault(player['team'], []).append(player['login'])
     return teams
    async def save_replay(self, game_id, stream):
        try:
            logger.debug(f"Saving replay {game_id}")
            await self._saver.save_replay(game_id, stream)
            logger.debug(f"Saved replay {game_id}")
            metrics.saved_replays.inc()
        except BookkeepingError as e:
            logger.warning(f"Failed to save replay for game {game_id}: {e}")

        try:
            logger.debug(f"Analyzing replay {game_id}")
            ticks = self._analyzer.get_replay_ticks(stream.data.bytes())
            logger.debug(f"Updating tick count for game {game_id}")
            await self._queries.update_game_stats(game_id, ticks)
        except BookkeepingError as e:
            logger.warning(f"Failed to analyze replay for game {game_id}: {e}")
 async def _handle_initial_data(self, connection):
     metric = metrics.active_conns.labels(category="initial")
     with metrics.track(metric):
         header = await self._header_read(connection)
         logger.debug(f"Accepted new connection: {header}")
         return header
    async def get_game_stats(self, game_id):
        """
        Gets the game information.
        """
        query = """
            SELECT
                `game_stats`.`startTime` AS start_time,
                `game_stats`.`endTime` AS end_time,
                `game_stats`.`gameType` AS game_type,
                `login`.`login` AS host,
                `game_stats`.`gameName` AS game_name,
                `game_featuredMods`.`gamemod` AS game_mod,
                `table_map`.`filename` AS file_name
            FROM `game_stats`
            LEFT JOIN `table_map`
              ON `game_stats`.`mapId` = `table_map`.`id`
            LEFT JOIN `login`
              ON `login`.id = `game_stats`.`host`
            LEFT JOIN  `game_featuredMods`
              ON `game_stats`.`gameMod` = `game_featuredMods`.`id`
            WHERE `game_stats`.`id` = %s
        """
        player_query = """
           SELECT COUNT(*) FROM `game_player_stats`
           WHERE `game_player_stats`.`gameId` = %s
        """
        logger.debug(f"Performing query: {query}")
        game_stats = await self._db.execute(query, (game_id, ))
        logger.debug(f"Performing query: {player_query}")
        player_count = await self._db.execute(player_query, (game_id, ))
        player_count = player_count[0]['COUNT(*)']
        if not game_stats:
            raise BookkeepingError(f"No stats found for game {game_id}")
        start_time = game_stats[0]['start_time'].timestamp()

        # 'mapname' is a filename on the content server containing the map
        mapname = game_stats[0]['file_name']
        if mapname is None:
            # Legacy replay server is forgiving like this. We replicate its
            # behaviour.
            logger.warning(f"Map missing for game {game_id}! Saving anyway.")
            mapname = "None"
        else:
            mapname = os.path.splitext(os.path.basename(mapname))[0]

        # We might end a replay before end_time is set in the db!
        end_time = game_stats[0]['end_time']
        if end_time is None:
            end_time = time.time()
        else:
            end_time = end_time.timestamp()
        return {
            'featured_mod': game_stats[0]['game_mod'],
            'game_type': game_stats[0]['game_type'],
            'recorder': game_stats[0]['host'],
            'host': game_stats[0]['host'],
            'launched_at': start_time,
            'game_end': end_time,
            'title': game_stats[0]['game_name'],
            'mapname': mapname,
            'num_players': player_count
        }
 async def _cleanup_connection(self, connection):
     connection.close()
     await connection.wait_closed()
     logger.debug(f"Finished serving connection: {connection}")
     self._connections.remove(connection)
 async def _handle_initial_data(self, connection):
     header = await self._header_read(connection)
     connection.add_header(header)
     logger.debug(f"Accepted new connection: {connection}")
     return header
 async def _remove_replay_when_done(self, game_id, replay):
     await replay.wait_for_ended()
     self._replays.pop(game_id, None)
     logger.debug(f"Replay removed: id {game_id}")
     metrics.running_replays.dec()
     metrics.finished_replays.inc()
 def _create(self, game_id):
     replay = self._replay_builder(game_id)
     self._replays[game_id] = replay
     asyncio.ensure_future(self._remove_replay_when_done(game_id, replay))
     logger.debug(f"New Replay created: id {game_id}")
     metrics.running_replays.inc()