コード例 #1
0
    def add_card(self, userid: UserID, cardid: str) -> None:
        """
        Given a user ID and a card ID, link that card with that user.

        Note that this is the E004 number as stored on the card. Not the 16 digit
        ASCII value on the back. Use CardCipher to convert.

        Parameters:
            userid - Integer user ID, as looked up by one of the above functions.
            cardid - 16-digit card ID to add.
        """
        sql = "INSERT INTO card (userid, id) VALUES (:userid, :cardid)"
        self.execute(sql, {'userid': userid, 'cardid': cardid})

        oldid = RemoteUser.card_to_userid(cardid)
        if RemoteUser.is_remote(oldid):
            # Kill any refid/extid that related to this card, since its now associated
            # with another existing account.
            sql = "DELETE FROM extid WHERE userid = :oldid"
            self.execute(sql, {'oldid': oldid})
            sql = "DELETE FROM refid WHERE userid = :oldid"
            self.execute(sql, {'oldid': oldid})

            # Point at the new account for any rivals against this card. Note that this
            # might result in a duplicate rival, but its a very small edge case.
            sql = "UPDATE link SET other_userid = :newid WHERE other_userid = :oldid"
            self.execute(sql, {'newid': userid, 'oldid': oldid})
コード例 #2
0
    def get_score(self, game: str, version: int, userid: UserID, songid: int,
                  songchart: int) -> Optional[Score]:
        # Helper function so we can iterate over all servers for a single card
        def get_scores_for_card(cardid: str) -> List[Score]:
            return Parallel.flatten(
                Parallel.call(
                    [client.get_records for client in self.clients],
                    game,
                    version,
                    APIConstants.ID_TYPE_INSTANCE,
                    [songid, songchart, cardid],
                ))

        relevant_cards = self.__get_cardids(userid)
        if RemoteUser.is_remote(userid):
            # No need to look up local score for this user
            scores = Parallel.flatten(
                Parallel.map(
                    get_scores_for_card,
                    relevant_cards,
                ))
            localscore = None
        else:
            localscore, scores = Parallel.execute([
                lambda: self.music.get_score(game, version, userid, songid,
                                             songchart),
                lambda: Parallel.flatten(
                    Parallel.map(
                        get_scores_for_card,
                        relevant_cards,
                    )),
            ])

        topscore = localscore

        for score in scores:
            if int(score['song']) != songid:
                continue
            if int(score['chart']) != songchart:
                continue

            newscore = self.__format_score(game, version, songid, songchart,
                                           score)

            if topscore is None:
                # No merging needed
                topscore = newscore
                continue

            topscore = self.__merge_score(game, version, topscore, newscore)

        return topscore
コード例 #3
0
    def create_account(self, cardid: str, pin: str) -> Optional[UserID]:
        """
        Given a Card ID and a PIN, create a new account.

        Parameters:
            cardid - 16-digit card ID of the card we are creating an account for.
            pin - Four digit PIN as entered by the user on a cabinet.

        Returns:
            A User ID if creation was successful, or None otherwise.
        """
        # First, create a user account
        sql = "INSERT INTO user (pin, admin) VALUES (:pin, 0)"
        cursor = self.execute(sql, {'pin': pin})
        if cursor.rowcount != 1:
            return None
        userid = cursor.lastrowid

        # Now, insert the card, tying it to the account
        sql = "INSERT INTO card (id, userid) VALUES (:cardid, :userid)"
        cursor = self.execute(sql, {'cardid': cardid, 'userid': userid})
        if cursor.rowcount != 1:
            return None

        # Now, if this user played on a remote network and their profile
        # was ever fetched locally or they were ever rivaled against,
        # convert those locally too so that players don't lose rivals
        # on new account creation.
        oldid = RemoteUser.card_to_userid(cardid)
        if RemoteUser.is_remote(oldid):
            sql = "UPDATE extid SET userid = :newid WHERE userid = :oldid"
            self.execute(sql, {'newid': userid, 'oldid': oldid})
            sql = "UPDATE refid SET userid = :newid WHERE userid = :oldid"
            self.execute(sql, {'newid': userid, 'oldid': oldid})
            sql = "UPDATE link SET other_userid = :newid WHERE other_userid = :oldid"
            self.execute(sql, {'newid': userid, 'oldid': oldid})

        # Finally, return the user ID
        return userid
コード例 #4
0
    def get_scores(
        self,
        game: str,
        version: int,
        userid: UserID,
        since: Optional[int] = None,
        until: Optional[int] = None,
    ) -> List[Score]:
        relevant_cards = self.__get_cardids(userid)
        if RemoteUser.is_remote(userid):
            # No need to look up local score for this user
            scores = Parallel.flatten(
                Parallel.call(
                    [client.get_records for client in self.clients],
                    game,
                    version,
                    APIConstants.ID_TYPE_CARD,
                    relevant_cards,
                    since,
                    until,
                ))
            localscores: List[Score] = []
        else:
            localscores, scores = Parallel.execute([
                lambda: self.music.get_scores(game, version, userid, since,
                                              until),
                lambda: Parallel.flatten(
                    Parallel.call(
                        [client.get_records for client in self.clients],
                        game,
                        version,
                        APIConstants.ID_TYPE_CARD,
                        relevant_cards,
                        since,
                        until,
                    )),
            ])

        allscores: Dict[int, Dict[int, Score]] = {}

        def add_score(score: Score) -> None:
            if score.id not in allscores:
                allscores[score.id] = {}
            allscores[score.id][score.chart] = score

        def get_score(songid: int, songchart: int) -> Optional[Score]:
            return allscores.get(songid, {}).get(songchart)

        # First, seed with local scores
        for score in localscores:
            add_score(score)

        # Second, merge in remote scorse
        for remotescore in scores:
            songid = int(remotescore['song'])
            chart = int(remotescore['chart'])
            newscore = self.__format_score(game, version, songid, chart,
                                           remotescore)
            oldscore = get_score(songid, chart)

            if oldscore is None:
                add_score(newscore)
            else:
                add_score(self.__merge_score(game, version, oldscore,
                                             newscore))

        # Finally, flatten and return
        finalscores: List[Score] = []
        for songid in allscores:
            for chart in allscores[songid]:
                finalscores.append(allscores[songid][chart])

        return finalscores
コード例 #5
0
 def __get_cardids(self, userid: UserID) -> List[str]:
     if RemoteUser.is_remote(userid):
         return [RemoteUser.userid_to_card(userid)]
     else:
         return self.user.get_cards(userid)
コード例 #6
0
ファイル: user.py プロジェクト: vangar/bemaniutils
    def get_any_profiles(self, game: str, version: int, userids: List[UserID]) -> List[Tuple[UserID, Optional[ValidatedDict]]]:
        if len(userids) == 0:
            return []

        remote_ids = [
            userid for userid in userids
            if RemoteUser.is_remote(userid)
        ]
        local_ids = [
            userid for userid in userids
            if not RemoteUser.is_remote(userid)
        ]

        if len(remote_ids) == 0:
            # We only have local profiles here, just pass on to the underlying layer
            return self.user.get_any_profiles(game, version, local_ids)
        else:
            # We have to fetch some local profiles and some remote profiles, and then
            # merge them together
            card_to_userid = {
                RemoteUser.userid_to_card(userid): userid
                for userid in remote_ids
            }

            local_profiles, remote_profiles = Parallel.execute([
                lambda: self.user.get_any_profiles(game, version, local_ids),
                lambda: Parallel.flatten(Parallel.call(
                    [client.get_profiles for client in self.clients],
                    game,
                    version,
                    APIConstants.ID_TYPE_CARD,
                    [RemoteUser.userid_to_card(userid) for userid in remote_ids],
                ))
            ])

            for profile in remote_profiles:
                cards = [card.upper() for card in profile.get('cards', [])]
                for card in cards:
                    # Map it back to the requested user
                    userid = card_to_userid.get(card)
                    if userid is None:
                        continue

                    # Sanitize the returned data
                    profile = copy.deepcopy(profile)
                    del profile['cards']

                    exact_match = profile.get('match', 'partial') == 'exact'

                    if 'match' in profile:
                        del profile['match']

                    refid = self.user.get_refid(game, version, userid)
                    extid = self.user.get_extid(game, version, userid)

                    # Add in our defaults we always provide
                    profile['game'] = game
                    profile['version'] = version if exact_match else 0
                    profile['refid'] = refid
                    profile['extid'] = extid

                    local_profiles.append(
                        (userid, self.__format_profile(ValidatedDict(profile))),
                    )

                    # Mark that we saw this card/user
                    del card_to_userid[card]

            # Finally, mark all missing remote profiles as None
            for card in card_to_userid:
                local_profiles.append((card_to_userid[card], None))

            return local_profiles
コード例 #7
0
ファイル: user.py プロジェクト: vangar/bemaniutils
 def get_any_profile(self, game: str, version: int, userid: UserID) -> Optional[ValidatedDict]:
     if RemoteUser.is_remote(userid):
         return self.__profile_request(game, version, userid, exact=False)
     else:
         return self.user.get_any_profile(game, version, userid)
コード例 #8
0
 def test_is_remote(self) -> None:
     self.assertTrue(RemoteUser.is_remote(UserID(2**64 - 1)))
     self.assertTrue(RemoteUser.is_remote(UserID(2**32)))
     self.assertFalse(RemoteUser.is_remote(UserID(2**32 - 1)))
     self.assertFalse(RemoteUser.is_remote(UserID(0)))
     self.assertFalse(RemoteUser.is_remote(UserID(1)))