Esempio n. 1
0
    def put_lobby(self, game: str, version: int, userid: UserID,
                  data: Dict[str, Any]) -> None:
        """
        Given a game, version and a user ID, save lobby information for that user.

        Parameters:
            game - String identifying a game series.
            version - Integer identifying the version of the game in the series.
            userid - Integer identifying a user.
            data - A dictionary of lobby information to store.
        """
        data = copy.deepcopy(data)
        if 'id' in data:
            del data['id']

        # Add json to lobby
        sql = ("INSERT INTO lobby (game, version, userid, time, data) " +
               "VALUES (:game, :version, :userid, :time, :data) " +
               "ON DUPLICATE KEY UPDATE time=VALUES(time), data=VALUES(data)")
        self.execute(
            sql,
            {
                'game': game,
                'version': version,
                'userid': userid,
                'time': Time.now(),
                'data': self.serialize(data),
            },
        )
Esempio n. 2
0
    def get_all_play_session_infos(
            self, game: str,
            version: int) -> List[Tuple[UserID, ValidatedDict]]:
        """
        Given a game and version, look up all play session information.

        Parameters:
            game - String identifying a game series.
            version - Integer identifying the version of the game in the series.

        Returns:
            A list of Tuples, consisting of a UserID and the dictionary that would be
            returned for that user if get_play_session_info() was called for that user.
        """
        sql = ("SELECT id, time, userid, data FROM playsession "
               "WHERE game = :game AND version = :version "
               "AND time > :time")
        cursor = self.execute(
            sql,
            {
                'game': game,
                'version': version,
                'time': Time.now() - Time.SECONDS_IN_HOUR,
            },
        )

        ret = []
        for result in cursor.fetchall():
            data = ValidatedDict(self.deserialize(result['data']))
            data['id'] = result['id']
            data['time'] = result['time']
            ret.append((UserID(result['userid']), data))
        return ret
Esempio n. 3
0
    def get_time_sensitive_settings(self, game: str, version: int,
                                    name: str) -> Optional[ValidatedDict]:
        """
        Given a game/version/name, look up the current time-sensitive settings for this game.

        Parameters:
            game - String identifier of the game we want settings for.
            version - Integer identifying the game version we want settings for.
            name - The name of the setting we are concerned with.

        Returns:
            A ValidatedDict of stored settings if the current setting is found, or None otherwise.
            If settings were found, they are guaranteed to include the attributes 'start_time' and
            'end_time' which will both be seconds since the unix epoch (UTC).
        """
        sql = (
            "SELECT data, start_time, end_time FROM time_sensitive_settings WHERE "
            "game = :game AND version = :version AND name = :name AND start_time <= :time AND end_time > :time"
        )
        cursor = self.execute(sql, {
            'game': game,
            'version': version,
            'name': name,
            'time': Time.now()
        })
        if cursor.rowcount != 1:
            # setting doesn't exist
            return None

        result = cursor.fetchone()
        retval = ValidatedDict(self.deserialize(result['data']))
        retval['start_time'] = result['start_time']
        retval['end_time'] = result['end_time']
        return retval
Esempio n. 4
0
    def destroy_play_session_info(self, game: str, version: int,
                                  userid: UserID) -> None:
        """
        Given a game, version and a user ID, throw away session info for that play session.

        Parameters:
            game - String identifying a game series.
            version - Integer identifying the version of the game in the series.
            userid - Integer identifying a user, as possibly looked up by UserData.
        """
        # Kill this play session
        sql = (
            "DELETE FROM playsession WHERE game = :game AND version = :version AND userid = :userid"
        )
        self.execute(
            sql,
            {
                'game': game,
                'version': version,
                'userid': userid,
            },
        )
        # Prune any orphaned lobbies too
        sql = "DELETE FROM playsession WHERE time <= :time"
        self.execute(sql, {'time': Time.now() - Time.SECONDS_IN_HOUR})
Esempio n. 5
0
    def get_all_lobbies(self, game: str,
                        version: int) -> List[Tuple[UserID, ValidatedDict]]:
        """
        Given a game and version, look up all active lobbies.

        Parameters:
            game - String identifying a game series.
            version - Integer identifying the version of the game in the series.

        Returns:
            A list of dictionaries representing lobby info stored by a game class.
        """
        sql = ("SELECT userid, id, data FROM lobby "
               "WHERE game = :game AND version = :version AND time > :time")
        cursor = self.execute(
            sql,
            {
                'game': game,
                'version': version,
                'time': Time.now() - Time.SECONDS_IN_HOUR,
            },
        )

        ret = []
        for result in cursor.fetchall():
            data = ValidatedDict(self.deserialize(result['data']))
            data['id'] = result['id']
            ret.append((UserID(result['userid']), data))
        return ret
Esempio n. 6
0
    def verify_event_w_update_status(self, loc: str, extid: int) -> None:
        call = self.call_node()

        event_w = Node.void('event_w')
        call.add_child(event_w)
        event_w.set_attribute('method', 'update_status')
        event_w.add_child(Node.s32('uid', extid))
        event_w.add_child(Node.string('p_name', self.NAME))
        event_w.add_child(Node.s32('exp', 0))
        event_w.add_child(Node.s32('customize', 0))
        event_w.add_child(Node.s32('tid', 0))
        event_w.add_child(Node.string('t_name', ''))
        event_w.add_child(Node.string('lid', loc))
        event_w.add_child(Node.string('s_name', ''))
        event_w.add_child(Node.s8('pref', 51))
        event_w.add_child(Node.s32('time', Time.now()))
        event_w.add_child(Node.s8('status', 1))
        event_w.add_child(Node.s8('stage', 0))
        event_w.add_child(Node.s32('mid', -1))
        event_w.add_child(Node.s8('ng', -1))

        # Swap with server
        resp = self.exchange('', call)

        # Verify that response is correct
        self.assert_path(resp, "response/event_w/@status")
Esempio n. 7
0
    def put_time_based_achievement(
        self,
        game: str,
        version: int,
        userid: UserID,
        achievementid: int,
        achievementtype: str,
        data: Dict[str, Any],
    ) -> None:
        """
        Given a game/version/userid and achievement id/type, save a time-based achievement. Assumes that
        time-based achievements are immutable once saved.

        Parameters:
            game - String identifier of the game looking up the user.
            version - Integer version of the game looking up the user.
            userid - Integer user ID, as looked up by one of the above functions.
            achievementid - Integer ID, as provided by a game.
            achievementtype - The type of achievement.
            data - A dictionary of data that the game wishes to retrieve later.
        """
        refid = self.get_refid(game, version, userid)

        # Add achievement JSON to achievements
        sql = (
            "INSERT INTO time_based_achievement (refid, id, type, timestamp, data) "
            + "VALUES (:refid, :id, :type, :ts, :data)")
        self.execute(
            sql, {
                'refid': refid,
                'id': achievementid,
                'type': achievementtype,
                'ts': Time.now(),
                'data': self.serialize(data)
            })
Esempio n. 8
0
    def verify_gameend_regist(
        self,
        ref_id: str,
        jid: int,
        scores: List[Dict[str, Any]],
    ) -> None:
        call = self.call_node()

        # Construct node
        gameend = Node.void('gameend')
        call.add_child(gameend)
        gameend.set_attribute('method', 'regist')
        gameend.add_child(Node.s32('retry', 0))
        pcbinfo = Node.void('pcbinfo')
        gameend.add_child(pcbinfo)
        pcbinfo.set_attribute('client_data_version', '0')
        data = Node.void('data')
        gameend.add_child(data)
        player = Node.void('player')
        data.add_child(player)
        player.add_child(Node.string('refid', ref_id))
        player.add_child(Node.s32('jid', jid))
        player.add_child(Node.string('name', self.NAME))
        result = Node.void('result')
        data.add_child(result)
        result.set_attribute('count', str(len(scores)))

        # Send scores
        scoreid = 0
        for score in scores:
            # Always played
            bits = 0x1
            if score['clear']:
                bits |= 0x2
            if score['fc']:
                bits |= 0x4
            if score['ex']:
                bits |= 0x8

            # Intentionally starting at 1 because that's what the game does
            scoreid = scoreid + 1
            tune = Node.void('tune')
            result.add_child(tune)
            tune.set_attribute('id', str(scoreid))
            tune.add_child(Node.s32('music', score['id']))
            tune.add_child(Node.s64('timestamp', Time.now() * 1000))
            player_1 = Node.void('player')
            tune.add_child(player_1)
            player_1.set_attribute('rank', '1')
            scorenode = Node.s32('score', score['score'])
            player_1.add_child(scorenode)
            scorenode.set_attribute('seq', str(score['chart']))
            scorenode.set_attribute('clear', str(bits))
            scorenode.set_attribute('combo', '69')
            player_1.add_child(Node.u8_array('mbar', [239, 175, 170, 170, 190, 234, 187, 158, 153, 230, 170, 90, 102, 170, 85, 150, 150, 102, 85, 234, 171, 169, 157, 150, 170, 101, 230, 90, 214, 255]))

        # Swap with server
        resp = self.exchange('', call)
        self.assert_path(resp, "response/gameend/data/player/session_id")
Esempio n. 9
0
    def put_attempt(
        self,
        game: str,
        version: int,
        userid: Optional[UserID],
        songid: int,
        songchart: int,
        location: int,
        points: int,
        data: Dict[str, Any],
        new_record: bool,
        timestamp: Optional[int] = None,
    ) -> None:
        """
        Given a game/version/song/chart and user ID, save a single score attempt.

        Note that this is different than put_score above, because a user may have only one score
        per song/chart in a given game, but they can have as many history entries as times played.

        Parameters:
            game - String representing a game series.
            version - Integer representing which version of the game.
            userid - Integer representing a user. Usually looked up with UserData.
            songid - ID of the song according to the game.
            songchart - Chart number according to the game.
            location - Machine ID where this score was earned.
            points - Points obtained on this song.
            data - Optional data that the game wishes to record along with the score.
            new_record - Whether this score was a new record or not.
            timestamp - Optional integer specifying when the attempt happened.
        """
        # First look up the song/chart from the music DB
        musicid = self.__get_musicid(game, version, songid, songchart)
        ts = timestamp if timestamp is not None else Time.now()

        # Add to score history
        sql = (
            "INSERT INTO `score_history` (userid, musicid, timestamp, lid, new_record, points, data) "
            +
            "VALUES (:userid, :musicid, :timestamp, :location, :new_record, :points, :data)"
        )
        try:
            self.execute(
                sql,
                {
                    'userid': userid if userid is not None else 0,
                    'musicid': musicid,
                    'timestamp': ts,
                    'location': location,
                    'new_record': 1 if new_record else 0,
                    'points': points,
                    'data': self.serialize(data),
                },
            )
        except IntegrityError:
            raise ScoreSaveException(
                f'There is already an attempt by {userid if userid is not None else 0} for music id {musicid} at {ts}'
            )
Esempio n. 10
0
    def handle_info_rb5_info_read_shop_ranking_request(self,
                                                       request: Node) -> Node:
        start_music_id = request.child_value('min')
        end_music_id = request.child_value('max')

        root = Node.void('info')
        shop_score = Node.void('shop_score')
        root.add_child(shop_score)
        shop_score.add_child(Node.s32('time', Time.now()))

        profiles: Dict[UserID, ValidatedDict] = {}
        for songid in range(start_music_id, end_music_id + 1):
            allscores = self.data.local.music.get_all_scores(
                self.game,
                self.music_version,
                songid=songid,
            )

            for ng in [
                    self.CHART_TYPE_BASIC,
                    self.CHART_TYPE_MEDIUM,
                    self.CHART_TYPE_HARD,
                    self.CHART_TYPE_SPECIAL,
            ]:
                scores = sorted(
                    [score for score in allscores if score[1].chart == ng],
                    key=lambda score: score[1].points,
                    reverse=True,
                )

                for i in range(len(scores)):
                    userid, score = scores[i]
                    if userid not in profiles:
                        profiles[userid] = self.get_any_profile(userid)
                    profile = profiles[userid]

                    data = Node.void('data')
                    shop_score.add_child(data)
                    data.add_child(Node.s32('rank', i + 1))
                    data.add_child(Node.s16('music_id', songid))
                    data.add_child(Node.s8('note_grade', score.chart))
                    data.add_child(
                        Node.s8(
                            'clear_type',
                            self._db_to_game_clear_type(
                                score.data.get_int('clear_type'))))
                    data.add_child(
                        Node.s32('user_id', profile.get_int('extid')))
                    data.add_child(
                        Node.s16(
                            'icon_id',
                            profile.get_dict('config').get_int('icon_id')))
                    data.add_child(Node.s32('score', score.points))
                    data.add_child(Node.s32('time', score.timestamp))
                    data.add_child(Node.string('name',
                                               profile.get_str('name')))

        return root
Esempio n. 11
0
    def get_schedule_duration(self, schedule: str) -> Tuple[int, int]:
        """
        Given a schedule type, returns the timestamp for the start and end
        of the current schedule of this type.
        """
        if schedule not in ['daily', 'weekly']:
            raise Exception(
                'Logic error, specify either \'daily\' or \'weekly\' for schedule type!'
            )

        if schedule == 'daily':
            return (Time.beginning_of_today(), Time.end_of_today())

        if schedule == 'weekly':
            return (Time.beginning_of_this_week(), Time.end_of_this_week())

        # Should never happen
        return (0, 0)
Esempio n. 12
0
    def verify_game_save(self, location: str, refid: str, packet: int,
                         block: int, exp: int) -> None:
        call = self.call_node()

        game = Node.void('game')
        call.add_child(game)
        game.set_attribute('method', 'save')
        game.set_attribute('refid', refid)
        game.set_attribute('locid', location)
        game.set_attribute('ver', '0')
        game.add_child(Node.u8('headphone', 0))
        game.add_child(Node.u8('hispeed', 16))
        game.add_child(Node.u16('appeal_id', 19))
        game.add_child(Node.u16('frame0', 0))
        game.add_child(Node.u16('frame1', 0))
        game.add_child(Node.u16('frame2', 0))
        game.add_child(Node.u16('frame3', 0))
        game.add_child(Node.u16('frame4', 0))
        last = Node.void('last')
        game.add_child(last)
        last.set_attribute('music_type', '1')
        last.set_attribute('music_id', '29')
        last.set_attribute('sort_type', '4')
        game.add_child(Node.u32('earned_gamecoin_packet', packet))
        game.add_child(Node.u32('earned_gamecoin_block', block))
        game.add_child(Node.u32('gain_exp', exp))
        game.add_child(Node.u32('m_user_cnt', 0))
        game.add_child(Node.bool_array('have_item', [False] * 512))
        game.add_child(Node.bool_array('have_note', [False] * 512))
        tracking = Node.void('tracking')
        game.add_child(tracking)
        m0 = Node.void('m0')
        tracking.add_child(m0)
        m0.add_child(Node.u8('type', 2))
        m0.add_child(Node.u32('id', 5))
        m0.add_child(Node.u32('score', 774566))
        tracking.add_child(Node.time('p_start', Time.now() - 300))
        tracking.add_child(Node.time('p_end', Time.now()))

        # Swap with server
        resp = self.exchange('', call)

        # Verify that response is correct
        self.assert_path(resp, "response/game")
Esempio n. 13
0
    def should_schedule(self, game: str, version: int, name: str,
                        schedule: str) -> bool:
        """
        Given a game/version/name pair and a schedule value, return whether
        this scheduled work is overdue or not.
        """
        if schedule not in ['daily', 'weekly']:
            raise Exception(
                'Logic error, specify either \'daily\' or \'weekly\' for schedule type!'
            )

        sql = ("SELECT year, day FROM scheduled_work "
               "WHERE game = :game AND version = :version AND "
               "name = :name AND schedule = :schedule")
        cursor = self.execute(sql, {
            'game': game,
            'version': version,
            'name': name,
            'schedule': schedule
        })
        if cursor.rowcount != 1:
            # No scheduled work was registered, so time to get going!
            return True

        result = cursor.fetchone()

        if schedule == 'daily':
            # Just look at the day and year, make sure it matches
            year, day = Time.days_into_year()
            if year != result['year']:
                # Wrong year, so we certainly need to run!
                return True
            if day != result['day']:
                # Wrong day and we're daily, so need to run!
                return True

        if schedule == 'weekly':
            # Find the beginning of the week (Monday), as days since epoch.
            if Time.week_in_days_since_epoch() != result['day']:
                # Wrong week, so we should run!
                return True

        # We have already run this work for this schedule
        return False
Esempio n. 14
0
    def handle_player_write_request(self, request: Node) -> Node:
        refid = request.child_value('rid')
        profile = self.put_profile_by_refid(refid, request)
        root = Node.void('player')

        if profile is None:
            root.add_child(Node.s32('uid', 0))
        else:
            root.add_child(Node.s32('uid', profile.get_int('extid')))
        root.add_child(Node.s32('time', Time.now()))
        return root
Esempio n. 15
0
    def mark_scheduled(self, game: str, version: int, name: str,
                       schedule: str) -> None:
        if schedule not in ['daily', 'weekly']:
            raise Exception(
                'Logic error, specify either \'daily\' or \'weekly\' for schedule type!'
            )

        if schedule == 'daily':
            year, day = Time.days_into_year()
            sql = (
                "INSERT INTO scheduled_work (game, version, name, schedule, year, day) "
                + "VALUES (:game, :version, :name, :schedule, :year, :day) " +
                "ON DUPLICATE KEY UPDATE year=VALUES(year), day=VALUES(day)")
            self.execute(
                sql,
                {
                    'game': game,
                    'version': version,
                    'name': name,
                    'schedule': schedule,
                    'year': year,
                    'day': day,
                },
            )

        if schedule == 'weekly':
            days = Time.week_in_days_since_epoch()
            sql = (
                "INSERT INTO scheduled_work (game, version, name, schedule, day) "
                + "VALUES (:game, :version, :name, :schedule, :day) " +
                "ON DUPLICATE KEY UPDATE day=VALUES(day)")
            self.execute(
                sql,
                {
                    'game': game,
                    'version': version,
                    'name': name,
                    'schedule': schedule,
                    'day': days,
                },
            )
Esempio n. 16
0
    def handle_ranking_read_request(self, request: Node) -> Node:
        root = Node.void('ranking')

        licenses = Node.void('lic_10')
        root.add_child(licenses)
        originals = Node.void('org_10')
        root.add_child(originals)

        licenses.add_child(Node.time('time', Time.now()))
        originals.add_child(Node.time('time', Time.now()))

        hitchart = self.data.local.music.get_hit_chart(self.game, self.version,
                                                       10)
        rank = 1
        for (mid, _plays) in hitchart:
            record = Node.void('record')
            originals.add_child(record)
            record.add_child(Node.s16('id', mid))
            record.add_child(Node.s16('rank', rank))
            rank = rank + 1

        return root
Esempio n. 17
0
    def destroy_lobby(self, lobbyid: int) -> None:
        """
        Given a lobby ID, destroy the lobby. The lobby ID can be obtained by reading
        the 'id' field of the get_lobby response.

        Parameters:
            lobbyid: Integer identifying a lobby.
        """
        # Delete this lobby
        sql = "DELETE FROM lobby WHERE id = :id"
        self.execute(sql, {'id': lobbyid})
        # Prune any orphaned lobbies too
        sql = "DELETE FROM lobby WHERE time <= :time"
        self.execute(sql, {'time': Time.now() - Time.SECONDS_IN_HOUR})
Esempio n. 18
0
    def verify_playerdata_usergamedata_advanced_rivalload(
            self, refid: str, loadflag: int) -> None:
        call = self.call_node()

        # Construct node
        playerdata = Node.void('playerdata')
        call.add_child(playerdata)
        playerdata.set_attribute('method', 'usergamedata_advanced')
        playerdata.add_child(Node.u32('retrycnt', 0))
        info = Node.void('info')
        playerdata.add_child(info)
        info.add_child(Node.s32('version', 1))
        data = Node.void('data')
        playerdata.add_child(data)
        data.add_child(Node.string('mode', 'rivalload'))
        data.add_child(Node.u64('targettime', Time.now() * 1000))
        data.add_child(Node.string('shoparea', '.'))
        data.add_child(Node.bool('isdouble', False))
        data.add_child(Node.s32('loadflag', loadflag))
        data.add_child(Node.s32('ddrcode', 0))
        data.add_child(Node.s64('gamesession', 123456))
        data.add_child(Node.string('refid', refid))
        data.add_child(Node.string('dataid', refid))
        data.add_child(Node.string('gamekind', 'MDX'))
        data.add_child(Node.string('pcbid', self.pcbid))
        data.add_child(Node.void('record'))

        # Swap with server
        resp = self.exchange('', call)

        # Verify that response is correct
        self.assert_path(resp, "response/playerdata/data/recordtype")
        if loadflag != 2:
            # As implemented, its possible for a machine not in an arcade to have scores.
            # So, if the test PCBID we're using isn't in an arcade, we won't fetch scores
            # for area records (flag 2), so don't check for these in that case.
            self.assert_path(resp, "response/playerdata/data/record/mcode")
            self.assert_path(resp, "response/playerdata/data/record/notetype")
            self.assert_path(resp, "response/playerdata/data/record/rank")
            self.assert_path(resp, "response/playerdata/data/record/clearkind")
            self.assert_path(resp, "response/playerdata/data/record/flagdata")
            self.assert_path(resp, "response/playerdata/data/record/name")
            self.assert_path(resp, "response/playerdata/data/record/area")
            self.assert_path(resp, "response/playerdata/data/record/code")
            self.assert_path(resp, "response/playerdata/data/record/score")
            self.assert_path(resp, "response/playerdata/data/record/ghostid")

        if resp.child_value('playerdata/data/recordtype') != loadflag:
            raise Exception('Invalid record type returned!')
Esempio n. 19
0
def run_scheduled_work(config: Dict[str, Any]) -> None:
    data = Data(config)

    # Only run scheduled work for enabled components
    enabled_factories: List[Any] = []
    enabled_caches: List[Any] = []
    if config.get('support', {}).get(GameConstants.IIDX, False):
        enabled_factories.append(IIDXFactory)
        enabled_caches.append(IIDXCache)
    if config.get('support', {}).get(GameConstants.POPN_MUSIC, False):
        enabled_factories.append(PopnMusicFactory)
        enabled_caches.append(PopnMusicCache)
    if config.get('support', {}).get(GameConstants.JUBEAT, False):
        enabled_factories.append(JubeatFactory)
        enabled_caches.append(JubeatCache)
    if config.get('support', {}).get(GameConstants.BISHI_BASHI, False):
        enabled_factories.append(BishiBashiFactory)
        enabled_caches.append(BishiBashiCache)
    if config.get('support', {}).get(GameConstants.DDR, False):
        enabled_factories.append(DDRFactory)
        enabled_caches.append(DDRCache)
    if config.get('support', {}).get(GameConstants.SDVX, False):
        enabled_factories.append(SoundVoltexFactory)
        enabled_caches.append(SoundVoltexCache)
    if config.get('support', {}).get(GameConstants.REFLEC_BEAT, False):
        enabled_factories.append(ReflecBeatFactory)
        enabled_caches.append(ReflecBeatCache)
    if config.get('support', {}).get(GameConstants.MUSECA, False):
        enabled_factories.append(MusecaFactory)
        enabled_caches.append(MusecaCache)

    # First, run any backend scheduled work
    for factory in enabled_factories:
        factory.run_scheduled_work(data, config)  # type: ignore

    # Now, warm the caches for the frontend
    for cache in enabled_caches:
        cache.preload(data, config)  # type: ignore

    # Now, possibly delete old log entries
    keep_duration = config.get('event_log_duration', 0)
    if keep_duration > 0:
        # Calculate timestamp of events we should delete
        oldest_event = Time.now() - keep_duration
        data.local.network.delete_events(oldest_event)
Esempio n. 20
0
    def handle_info_rb5_info_read_hit_chart_request(self,
                                                    request: Node) -> Node:
        version = request.child_value('ver')

        root = Node.void('info')
        root.add_child(Node.s32('ver', version))
        ranking = Node.void('ranking')
        root.add_child(ranking)

        def add_hitchart(name: str, start: int, end: int,
                         hitchart: List[Tuple[int, int]]) -> None:
            base = Node.void(name)
            ranking.add_child(base)
            base.add_child(Node.s32('bt', start))
            base.add_child(Node.s32('et', end))
            new = Node.void('new')
            base.add_child(new)

            for (mid, plays) in hitchart:
                d = Node.void('d')
                new.add_child(d)
                d.add_child(Node.s16('mid', mid))
                d.add_child(Node.s32('cnt', plays))

        # Weekly hit chart
        add_hitchart(
            'weekly',
            Time.now() - Time.SECONDS_IN_WEEK,
            Time.now(),
            self.data.local.music.get_hit_chart(self.game, self.music_version,
                                                1024, 7),
        )

        # Monthly hit chart
        add_hitchart(
            'monthly',
            Time.now() - Time.SECONDS_IN_DAY * 30,
            Time.now(),
            self.data.local.music.get_hit_chart(self.game, self.music_version,
                                                1024, 30),
        )

        # All time hit chart
        add_hitchart(
            'total',
            Time.now() - Time.SECONDS_IN_DAY * 365,
            Time.now(),
            self.data.local.music.get_hit_chart(self.game, self.music_version,
                                                1024, 365),
        )

        return root
Esempio n. 21
0
    def _destroy_session(self, session: str, sesstype: str) -> None:
        """
        Destroy a previously-created session.

        Parameters:
            session - A session string as returned from create_session.
        """
        # Remove the session token
        sql = "DELETE FROM session WHERE session = :session AND type = :sesstype"
        self.execute(sql, {
            'session': session,
            'sesstype': sesstype
        },
                     safe_write_operation=True)

        # Also weed out any other defunct sessions
        sql = "DELETE FROM session WHERE expiration < :timestamp"
        self.execute(sql, {'timestamp': Time.now()}, safe_write_operation=True)
Esempio n. 22
0
    def create_news(self, title: str, body: str) -> int:
        """
        Given a title and body, create a new news entry.

        Parameters:
            title - String title of the entry.
            body - String body of the entry, may contain HTML.

        Returns:
            The ID of the newly created entry.
        """
        sql = "INSERT INTO news (timestamp, title, body) VALUES (:timestamp, :title, :body)"
        cursor = self.execute(sql, {
            'timestamp': int(Time.now()),
            'title': title,
            'body': body
        })
        return cursor.lastrowid
Esempio n. 23
0
    def get_hit_chart(
        self,
        game: str,
        version: int,
        count: int,
        days: Optional[int] = None,
    ) -> List[Tuple[int, int]]:
        """
        Look up a game's most played songs.

        Parameters:
            game - String representing a game series.
            version - Integer representing which version of the game.
            count - Number of scores to look up.

        Returns:
            A list of tuples, containing the songid and the number of plays across all charts for that song.
        """
        sql = (
            "SELECT music.songid AS songid, COUNT(score_history.timestamp) AS plays FROM score_history, music "
            +
            "WHERE score_history.musicid = music.id AND music.game = :game AND music.version = :version "
        )
        if days is not None:
            # Only select the last X days of hit chart
            sql = sql + "AND score_history.timestamp > :timestamp "
            timestamp = Time.now() - (Time.SECONDS_IN_DAY * days)
        else:
            timestamp = None

        sql = sql + "GROUP BY songid ORDER BY plays DESC LIMIT :count"
        cursor = self.execute(
            sql, {
                'game': game,
                'version': version,
                'count': count,
                'timestamp': timestamp
            })

        most_played = []
        for result in cursor.fetchall():
            most_played.append((result['songid'], result['plays']))

        return most_played
Esempio n. 24
0
 def put_event(
     self,
     event: str,
     data: Dict[str, Any],
     timestamp: Optional[int] = None,
     userid: Optional[UserID] = None,
     arcadeid: Optional[ArcadeID] = None,
 ) -> None:
     if timestamp is None:
         timestamp = Time.now()
     sql = "INSERT INTO audit (timestamp, userid, arcadeid, type, data) VALUES (:ts, :uid, :aid, :type, :data)"
     self.execute(
         sql, {
             'ts': timestamp,
             'type': event,
             'data': self.serialize(data),
             'uid': userid,
             'aid': arcadeid
         })
Esempio n. 25
0
    def create_client(self, name: str) -> int:
        """
        Given a name, create a new client and generate an authorization token.

        Parameters:
            name - String name of the client.

        Returns:
            The ID of the newly created client.
        """
        sql = "INSERT INTO client (timestamp, name, token) VALUES (:timestamp, :name, :token)"
        cursor = self.execute(
            sql,
            {
                'timestamp': int(Time.now()),
                'name': name,
                'token': str(uuid.uuid4()),
            },
        )
        return cursor.lastrowid
Esempio n. 26
0
    def create_server(self, uri: str, token: str) -> int:
        """
        Given a uri and a token, create a new server.

        Parameters:
            uri - String name of the server.
            token - Authorization token we will use when talking to the server.

        Returns:
            The ID of the newly created server.
        """
        sql = "INSERT INTO server (timestamp, uri, token, config) VALUES (:timestamp, :uri, :token, 0)"
        cursor = self.execute(
            sql,
            {
                'timestamp': int(Time.now()),
                'uri': uri,
                'token': token,
            },
        )
        return cursor.lastrowid
Esempio n. 27
0
    def handle_game_load_daily_request(self, request: Node) -> Node:
        extid = intish(request.attribute('code'))
        refid = request.attribute('refid')
        game = Node.void('game')
        profiledict = None

        if extid is not None:
            # Rival daily loading
            userid = self.data.remote.user.from_extid(self.game, self.version, extid)
        else:
            # Self daily loading
            userid = self.data.remote.user.from_refid(self.game, self.version, refid)
        if userid is not None:
            profiledict = self.get_profile(userid)

        if profiledict is not None:
            play_stats = self.get_play_statistics(userid)

            # Day play counts
            last_play_date = play_stats.get_int_array('last_play_date', 3)
            today_play_date = Time.todays_date()
            if (
                last_play_date[0] == today_play_date[0] and
                last_play_date[1] == today_play_date[1] and
                last_play_date[2] == today_play_date[2]
            ):
                today_count = play_stats.get_int('today_plays', 0)
            else:
                today_count = 0
            daycount = Node.void('daycount')
            game.add_child(daycount)
            daycount.set_attribute('playcount', str(today_count))

            # Daily combo stuff, unclear how this works
            dailycombo = Node.void('dailycombo')
            game.add_child(dailycombo)
            dailycombo.set_attribute('daily_combo', str(0))
            dailycombo.set_attribute('daily_combo_lv', str(0))

        return game
Esempio n. 28
0
    def _create_session(self,
                        opid: int,
                        optype: str,
                        expiration: int = (30 * 86400)) -> str:
        """
        Given an ID, create a session string.

        Parameters:
            opid - ID we wish to start a session for.
            expiration - Number of seconds before this session is invalid.

        Returns:
            A string that can be used as a session ID.
        """
        # Create a new session that is unique
        while True:
            session = ''.join(
                random.choice('0123456789ABCDEF')
                for _ in range(BaseData.SESSION_LENGTH))
            sql = "SELECT session FROM session WHERE session = :session"
            cursor = self.execute(sql, {'session': session})
            if cursor.rowcount == 0:
                # Make sure sessions expire in a reasonable amount of time
                expiration = Time.now() + expiration

                # Use that session
                sql = ("INSERT INTO session (id, session, type, expiration) " +
                       "VALUES (:id, :session, :optype, :expiration)")
                cursor = self.execute(
                    sql,
                    {
                        'id': opid,
                        'session': session,
                        'optype': optype,
                        'expiration': expiration
                    },
                    safe_write_operation=True,
                )
                if cursor.rowcount == 1:
                    return session
Esempio n. 29
0
    def get_play_session_info(self, game: str, version: int,
                              userid: UserID) -> Optional[ValidatedDict]:
        """
        Given a game, version and a user ID, look up play session information for that user.

        Parameters:
            game - String identifying a game series.
            version - Integer identifying the version of the game in the series.
            userid - Integer identifying a user, as possibly looked up by UserData.

        Returns:
            A dictionary representing play session info stored by a game class, or None
            if there is no active session for this game/version/user. The dictionary will
            always contain an 'id' field which is the play session ID, and a 'time' field
            which represents the timestamp when the play session began.
        """
        sql = (
            "SELECT id, time, data FROM playsession "
            "WHERE game = :game AND version = :version AND userid = :userid "
            "AND time > :time")
        cursor = self.execute(
            sql,
            {
                'game': game,
                'version': version,
                'userid': userid,
                'time': Time.now() - Time.SECONDS_IN_HOUR,
            },
        )

        if cursor.rowcount != 1:
            # Settings doesn't exist
            return None

        result = cursor.fetchone()
        data = ValidatedDict(self.deserialize(result['data']))
        data['id'] = result['id']
        data['time'] = result['time']
        return data
Esempio n. 30
0
def login() -> Response:
    username = request.form['username']
    password = request.form['password']

    userid = g.data.local.user.from_username(username)
    if userid is None:
        error('Unrecognized username or password!')
        return Response(render_template('account/login.html', **{'title': 'Log In', 'show_navigation': False, 'username': username}))

    if g.data.local.user.validate_password(userid, password):
        aes = AESCipher(g.config['secret_key'])
        sessionID = g.data.local.user.create_session(userid, expiration=90 * 86400)
        response = make_response(redirect(url_for('home_pages.viewhome')))
        response.set_cookie(
            'SessionID',
            aes.encrypt(sessionID),
            expires=Time.now() + (90 * Time.SECONDS_IN_DAY),
        )
        return response
    else:
        error('Unrecognized username or password!')
        return Response(render_template('account/login.html', **{'title': 'Log In', 'show_navigation': False, 'username': username}))