예제 #1
0
    def update_score(
        self,
        userid: UserID,
        timestamp: int,
        songid: int,
        chart: int,
        points: int,
        medal: int,
        combo: int,
        ghost: Optional[List[int]] = None,
        stats: Optional[Dict[str, int]] = None,
        music_rate: int = None,
    ) -> None:
        """
        Given various pieces of a score, update the user's high score and score
        history in a controlled manner, so all games in Jubeat series can expect
        the same attributes in a score.
        """
        # Range check medals
        if medal not in [
                self.PLAY_MEDAL_FAILED,
                self.PLAY_MEDAL_CLEARED,
                self.PLAY_MEDAL_NEARLY_FULL_COMBO,
                self.PLAY_MEDAL_FULL_COMBO,
                self.PLAY_MEDAL_NEARLY_EXCELLENT,
                self.PLAY_MEDAL_EXCELLENT,
        ]:
            raise Exception(f"Invalid medal value {medal}")

        oldscore = self.data.local.music.get_score(
            self.game,
            self.music_version,
            userid,
            songid,
            chart,
        )

        # Score history is verbatum, instead of highest score
        history = ValidatedDict({})
        oldpoints = points

        if oldscore is None:
            # If it is a new score, create a new dictionary to add to
            scoredata = ValidatedDict({})
            raised = True
            highscore = True
        else:
            # Set the score to any new record achieved
            raised = points > oldscore.points
            highscore = points >= oldscore.points
            points = max(oldscore.points, points)
            scoredata = oldscore.data

        # Replace medal with highest value
        scoredata.replace_int('medal', max(scoredata.get_int('medal'), medal))
        history.replace_int('medal', medal)

        # Increment counters based on medal
        if medal == self.PLAY_MEDAL_CLEARED:
            scoredata.increment_int('clear_count')
        if medal == self.PLAY_MEDAL_FULL_COMBO:
            scoredata.increment_int('full_combo_count')
        if medal == self.PLAY_MEDAL_EXCELLENT:
            scoredata.increment_int('excellent_count')

        # If we have a combo, replace it
        scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
        history.replace_int('combo', combo)

        if stats is not None:
            if raised:
                # We have stats, and there's a new high score, update the stats
                scoredata.replace_dict('stats', stats)
            history.replace_dict('stats', stats)

        if ghost is not None:
            # Update the ghost regardless, but don't bother with it in history
            scoredata.replace_int_array('ghost', len(ghost), ghost)

        if music_rate is not None:
            if oldscore is not None:
                if music_rate > oldscore.data.get_int('music_rate'):
                    scoredata.replace_int('music_rate', music_rate)
            else:
                scoredata.replace_int('music_rate', music_rate)
            history.replace_int('music_rate', music_rate)

        # Look up where this score was earned
        lid = self.get_machine_id()

        # Write the new score back
        self.data.local.music.put_score(
            self.game,
            self.music_version,
            userid,
            songid,
            chart,
            lid,
            points,
            scoredata,
            highscore,
            timestamp=timestamp,
        )

        # Save the history of this score too
        self.data.local.music.put_attempt(
            self.game,
            self.music_version,
            userid,
            songid,
            chart,
            lid,
            oldpoints,
            history,
            raised,
            timestamp=timestamp,
        )
예제 #2
0
    def update_score(
        self,
        userid: Optional[UserID],
        songid: int,
        chart: int,
        points: int,
        rank: int,
        halo: int,
        combo: int,
        trace: Optional[List[int]] = None,
        ghost: Optional[str] = None,
    ) -> None:
        """
        Given various pieces of a score, update the user's high score and score
        history in a controlled manner, so all games in DDR series can expect
        the same attributes in a score.
        """
        if chart not in [
                self.CHART_SINGLE_BEGINNER,
                self.CHART_SINGLE_BASIC,
                self.CHART_SINGLE_DIFFICULT,
                self.CHART_SINGLE_EXPERT,
                self.CHART_SINGLE_CHALLENGE,
                self.CHART_DOUBLE_BEGINNER,
                self.CHART_DOUBLE_BASIC,
                self.CHART_DOUBLE_DIFFICULT,
                self.CHART_DOUBLE_EXPERT,
                self.CHART_DOUBLE_CHALLENGE,
        ]:
            raise Exception('Invalid chart {}'.format(chart))
        if halo not in [
                self.HALO_NONE,
                self.HALO_GOOD_FULL_COMBO,
                self.HALO_GREAT_FULL_COMBO,
                self.HALO_PERFECT_FULL_COMBO,
                self.HALO_MARVELOUS_FULL_COMBO,
        ]:
            raise Exception('Invalid halo {}'.format(halo))
        if rank not in [
                self.RANK_E,
                self.RANK_D,
                self.RANK_D_PLUS,
                self.RANK_C_MINUS,
                self.RANK_C,
                self.RANK_C_PLUS,
                self.RANK_B_MINUS,
                self.RANK_B,
                self.RANK_B_PLUS,
                self.RANK_A_MINUS,
                self.RANK_A,
                self.RANK_A_PLUS,
                self.RANK_AA_MINUS,
                self.RANK_AA,
                self.RANK_AA_PLUS,
                self.RANK_AAA,
        ]:
            raise Exception('Invalid rank {}'.format(rank))

        if userid is not None:
            oldscore = self.data.local.music.get_score(
                self.game,
                self.music_version,
                userid,
                songid,
                chart,
            )
        else:
            oldscore = None

        # Score history is verbatum, instead of highest score
        now = Time.now()
        history = ValidatedDict({})
        oldpoints = points

        if oldscore is None:
            # If it is a new score, create a new dictionary to add to
            scoredata = ValidatedDict({})
            raised = True
            highscore = True
        else:
            # Set the score to any new record achieved
            raised = points > oldscore.points
            highscore = points >= oldscore.points
            points = max(oldscore.points, points)
            scoredata = oldscore.data

        # Save combo
        history.replace_int('combo', combo)
        scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))

        # Save halo
        history.replace_int('halo', halo)
        scoredata.replace_int('halo', max(scoredata.get_int('halo'), halo))

        # Save rank
        history.replace_int('rank', rank)
        scoredata.replace_int('rank', max(scoredata.get_int('rank'), rank))

        # Save ghost steps
        if trace is not None:
            history.replace_int_array('trace', len(trace), trace)
            if raised:
                scoredata.replace_int_array('trace', len(trace), trace)
        if ghost is not None:
            history.replace_str('ghost', ghost)
            if raised:
                scoredata.replace_str('ghost', ghost)

        # Look up where this score was earned
        lid = self.get_machine_id()

        # DDR sometimes happens to send all songs that were played by a player
        # at the end of the round. It sends timestamps for the songs, but as of
        # Colette they were identical for each song in the round. So, if a user
        # plays the same song/chart# more than once in a round, we will end up
        # failing to store the attempt since we don't allow two of the same
        # attempt at the same time for the same user and song/chart. So, bump
        # the timestamp by one second and retry well past the maximum number of
        # songs.
        for bump in range(10):
            timestamp = now + bump

            if userid is not None:
                # Write the new score back
                self.data.local.music.put_score(
                    self.game,
                    self.music_version,
                    userid,
                    songid,
                    chart,
                    lid,
                    points,
                    scoredata,
                    highscore,
                    timestamp=timestamp,
                )

            try:
                # Save the history of this score too
                self.data.local.music.put_attempt(
                    self.game,
                    self.music_version,
                    userid,
                    songid,
                    chart,
                    lid,
                    oldpoints,
                    history,
                    raised,
                    timestamp=timestamp,
                )
            except ScoreSaveException:
                # Try again one second in the future
                continue

            # We saved successfully
            break