Exemple #1
0
    def handle_game_load_c_request(self, request: Node) -> Node:
        extid = intish(request.attribute('code'))
        refid = request.attribute('refid')

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

        coursedata = [0] * 3200
        if userid is not None:
            for course in self.data.local.user.get_achievements(self.game, self.version, userid):
                if course.type != 'course':
                    continue

                # Grab course ID and chart (kinda pointless because we add it right back up
                # below, but it is more documented/readable this way.
                courseid = int(course.id / 4)
                coursechart = course.id % 4

                # Populate course data
                index = ((courseid * 4) + coursechart) * 8
                if index >= 0 and index <= (len(coursedata) - 8):
                    coursedata[index + 0] = int(course.data.get_int('score') / 10000)
                    coursedata[index + 1] = course.data.get_int('score') % 10000
                    coursedata[index + 2] = course.data.get_int('combo')
                    coursedata[index + 3] = self.db_to_game_rank(course.data.get_int('rank'))
                    coursedata[index + 5] = course.data.get_int('stage')
                    coursedata[index + 6] = course.data.get_int('combo_type')

        game = Node.void('game')
        game.add_child(Node.u16_array('course', coursedata))
        return game
Exemple #2
0
    def handle_game_trace_request(self, request: Node) -> Node:
        # This is almost identical to 2013 and below, except it will never
        # even try to request course traces, so we fork from common functionality.
        extid = int(request.attribute('code'))
        chart = int(request.attribute('type'))
        mid = intish(request.attribute('mid'))

        # Base packet is just game, if we find something we add to it
        game = Node.void('game')

        # Rival trace loading
        userid = self.data.remote.user.from_extid(self.game, self.version,
                                                  extid)
        if userid is None:
            # Nothing to load
            return game

        # Load trace from song score
        songscore = self.data.remote.music.get_score(
            self.game,
            self.music_version,
            userid,
            mid,
            self.game_to_db_chart(chart),
        )
        if songscore is not None and 'trace' in songscore.data:
            game.add_child(Node.u32('size', len(songscore.data['trace'])))
            game.add_child(Node.u8_array('trace', songscore.data['trace']))

        return game
Exemple #3
0
    def handle_game_score_request(self, request: Node) -> Node:
        refid = request.attribute('refid')
        songid = int(request.attribute('mid'))
        chart = self.game_to_db_chart(int(request.attribute('type')))

        userid = self.data.remote.user.from_refid(self.game, self.version, refid)
        if userid is not None:
            attempts = self.data.local.music.get_all_attempts(
                self.game,
                self.music_version,
                userid,
                songid=songid,
                songchart=chart,
                limit=5,
            )
            recentscores = [attempt.points for (_, attempt) in attempts]
        else:
            recentscores = []

        # Always pad to five, so we ensure that we return all the scores
        while len(recentscores) < 5:
            recentscores.append(0)

        # Return the most recent five scores
        game = Node.void('game')
        for i in range(len(recentscores)):
            game.set_attribute(f'sc{i + 1}', str(recentscores[i]))
        return game
Exemple #4
0
    def handle_game_new_request(self, request: Node) -> Node:
        refid = request.attribute('refid')
        name = request.attribute('name')
        loc = ID.parse_machine_id(request.attribute('locid'))
        self.new_profile_by_refid(refid, name, loc)

        root = Node.void('game')
        return root
Exemple #5
0
    def handle_game_save_c_request(self, request: Node) -> Node:
        refid = request.attribute('refid')
        courseid = int(request.attribute('cid'))
        chart = int(request.attribute('ctype'))

        userid = self.data.remote.user.from_refid(self.game, self.version, refid)
        if userid is not None:
            # Calculate statistics
            data = request.child('data')
            points = int(data.attribute('score'))
            combo = int(data.attribute('combo'))
            combo_type = int(data.attribute('combo_type'))
            stage = int(data.attribute('stage'))
            rank = self.game_to_db_rank(int(data.attribute('rank')))
            trace = request.child_value('trace')

            # Grab the old course score
            oldcourse = self.data.local.user.get_achievement(
                self.game,
                self.version,
                userid,
                (courseid * 4) + chart,
                'course',
            )

            if oldcourse is not None:
                highscore = points > oldcourse.get_int('score')

                points = max(points, oldcourse.get_int('score'))
                combo = max(combo, oldcourse.get_int('combo'))
                stage = max(stage, oldcourse.get_int('stage'))
                rank = max(rank, oldcourse.get_int('rank'))
                combo_type = max(combo_type, oldcourse.get_int('combo_type'))

                if not highscore:
                    # Don't overwrite the ghost for a non-highscore
                    trace = oldcourse.get_int_array('trace', len(trace))

            self.data.local.user.put_achievement(
                self.game,
                self.version,
                userid,
                (courseid * 4) + chart,
                'course',
                {
                    'score': points,
                    'combo': combo,
                    'stage': stage,
                    'rank': rank,
                    'combo_type': combo_type,
                    'trace': trace,
                },
            )

        # No response needed
        game = Node.void('game')
        return game
Exemple #6
0
    def handle_game_load_m_request(self, request: Node) -> Node:
        extid = intish(request.attribute('code'))
        refid = request.attribute('refid')

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

        if userid is not None:
            scores = self.data.remote.music.get_scores(self.game,
                                                       self.music_version,
                                                       userid)
        else:
            scores = []

        sortedscores: Dict[int, Dict[int, Score]] = {}
        for score in scores:
            if score.id not in sortedscores:
                sortedscores[score.id] = {}
            sortedscores[score.id][score.chart] = score

        game = Node.void('game')
        for song in sortedscores:
            music = Node.void('music')
            game.add_child(music)
            music.set_attribute('reclink', str(song))

            for chart in sortedscores[song]:
                score = sortedscores[song][chart]
                try:
                    gamechart = self.db_to_game_chart(chart)
                except KeyError:
                    # Don't support this chart in this game
                    continue
                gamerank = self.db_to_game_rank(score.data.get_int('rank'))
                combo_type = self.db_to_game_halo(score.data.get_int('halo'))

                typenode = Node.void('type')
                music.add_child(typenode)
                typenode.set_attribute('diff', str(gamechart))

                typenode.add_child(Node.u32('score', score.points))
                typenode.add_child(Node.u16('count', score.plays))
                typenode.add_child(Node.u8('rank', gamerank))
                typenode.add_child(Node.u8('combo_type', combo_type))
                # The game optionally receives hard, life8, life4, risky, assist_clear, normal_clear
                # u8 values too, and saves music scores with these set, but the UI doesn't appear to
                # do anything with them, so we don't care.

        return game
Exemple #7
0
    def handle_game_new_request(self, request: Node) -> Node:
        refid = request.attribute('refid')
        area = int(request.attribute('area'))
        name = request.attribute('name').strip()

        # Create a new profile for this user!
        self.new_profile_by_refid(refid, name, area)

        # No response needed
        game = Node.void('game')
        return game
Exemple #8
0
    def handle_game_request(self, request: Node) -> Optional[Node]:
        method = request.attribute('method')

        if method == 'get':
            # TODO: Hook these up to config so we can change this
            root = Node.void('game')
            root.add_child(Node.s32('ir_phase', 0))
            root.add_child(Node.s32('music_open_phase', 8))
            root.add_child(Node.s32('collabo_phase', 8))
            root.add_child(Node.s32('personal_event_phase', 10))
            root.add_child(Node.s32('shop_event_phase', 6))
            root.add_child(Node.s32('netvs_phase', 0))
            root.add_child(Node.s32('card_phase', 9))
            root.add_child(Node.s32('other_phase', 9))
            root.add_child(Node.s32('local_matching_enable', 1))
            root.add_child(Node.s32('n_matching_sec', 60))
            root.add_child(Node.s32('l_matching_sec', 60))
            root.add_child(Node.s32('is_check_cpu', 0))
            root.add_child(Node.s32('week_no', 0))
            root.add_child(Node.s16_array('sel_ranking', [-1, -1, -1, -1, -1]))
            root.add_child(Node.s16_array('up_ranking', [-1, -1, -1, -1, -1]))
            return root

        if method == 'active':
            # Update the name of this cab for admin purposes
            self.update_machine_name(request.child_value('shop_name'))
            return Node.void('game')

        if method == 'taxphase':
            return Node.void('game')

        # Invalid method
        return None
Exemple #9
0
 def handle_game_load_request(self, request: Node) -> Node:
     refid = request.attribute('dataid')
     root = self.get_profile_by_refid(refid)
     if root is None:
         root = Node.void('game')
         root.set_attribute('none', '1')
     return root
Exemple #10
0
    def handle_game_save_request(self, request: Node) -> Node:
        refid = request.attribute('refid')
        self.put_profile_by_refid(refid, request)

        # No response needed
        game = Node.void('game')
        return game
Exemple #11
0
    def handle_game_save_m_request(self, request: Node) -> Node:
        refid = request.attribute('dataid')

        if refid is not None:
            userid = self.data.remote.user.from_refid(self.game, self.version,
                                                      refid)
        else:
            userid = None

        if userid is None:
            return Node.void('game')

        musicid = int(request.attribute('music_id'))
        chart = int(request.attribute('music_type'))
        score = int(request.attribute('score'))
        combo = int(request.attribute('max_chain'))
        grade = self.__game_to_db_grade(int(request.attribute('score_grade')))
        clear_type = self.__game_to_db_clear_type(
            int(request.attribute('clear_type')))

        # Save the score
        self.update_score(
            userid,
            musicid,
            chart,
            score,
            clear_type,
            grade,
            combo,
        )

        # No response necessary
        return Node.void('game')
Exemple #12
0
    def handle_game_load_request(self, request: Node) -> Node:
        refid = request.attribute('refid')
        profile = self.get_profile_by_refid(refid)
        if profile is not None:
            return profile

        game = Node.void('game')
        game.set_attribute('none', '0')
        return game
Exemple #13
0
    def handle_game_trace_request(self, request: Node) -> Node:
        extid = int(request.attribute('code'))
        chart = int(request.attribute('type'))
        cid = intish(request.attribute('cid'))
        mid = intish(request.attribute('mid'))

        # Base packet is just game, if we find something we add to it
        game = Node.void('game')

        # Rival trace loading
        userid = self.data.remote.user.from_extid(self.game, self.version, extid)
        if userid is None:
            # Nothing to load
            return game

        if mid is not None:
            # Load trace from song score
            songscore = self.data.remote.music.get_score(
                self.game,
                self.music_version,
                userid,
                mid,
                self.game_to_db_chart(chart),
            )
            if songscore is not None and 'trace' in songscore.data:
                game.add_child(Node.u32('size', len(songscore.data['trace'])))
                game.add_child(Node.u8_array('trace', songscore.data['trace']))

        elif cid is not None:
            # Load trace from achievement
            coursescore = self.data.local.user.get_achievement(
                self.game,
                self.version,
                userid,
                (cid * 4) + chart,
                'course',
            )
            if coursescore is not None and 'trace' in coursescore:
                game.add_child(Node.u32('size', len(coursescore['trace'])))
                game.add_child(Node.u8_array('trace', coursescore['trace']))

        # Nothing found, return empty
        return game
Exemple #14
0
    def handle_game_request(self, request: Node) -> Optional[Node]:
        method = request.attribute('method')

        if method == 'get':
            # TODO: Hook these up to config so we can change this
            root = Node.void('game')
            root.set_attribute('game_phase', '2')
            root.set_attribute('psp_phase', '2')
            return root

        if method == 'active':
            # Update the name of this cab for admin purposes
            self.update_machine_name(request.attribute('shop_name'))
            return Node.void('game')

        if method == 'taxphase':
            return Node.void('game')

        # Invalid method
        return None
Exemple #15
0
    def handle_game_save_m_request(self, request: Node) -> Node:
        refid = request.attribute('refid')
        songid = int(request.attribute('mid'))
        chart = self.game_to_db_chart(int(request.attribute('mtype')))

        # Calculate statistics
        data = request.child('data')
        points = int(data.attribute('score'))
        combo = int(data.attribute('combo'))
        rank = self.game_to_db_rank(int(data.attribute('rank')))
        if points == 1000000:
            halo = self.HALO_MARVELOUS_FULL_COMBO
        elif int(data.attribute('perf_fc')) != 0:
            halo = self.HALO_PERFECT_FULL_COMBO
        elif int(data.attribute('great_fc')) != 0:
            halo = self.HALO_GREAT_FULL_COMBO
        elif int(data.attribute('good_fc')) != 0:
            halo = self.HALO_GOOD_FULL_COMBO
        else:
            halo = self.HALO_NONE
        trace = request.child_value('trace')

        # Save the score, regardless of whether we have a refid. If we save
        # an anonymous score, it only goes into the DB to count against the
        # number of plays for that song/chart.
        userid = self.data.remote.user.from_refid(self.game, self.version,
                                                  refid)
        self.update_score(
            userid,
            songid,
            chart,
            points,
            rank,
            halo,
            combo,
            trace,
        )

        # No response needed
        game = Node.void('game')
        return game
Exemple #16
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
Exemple #17
0
    def handle_game_area_hiscore_request(self, request: Node) -> Node:
        shop_area = int(request.attribute('shop_area'))

        # First, get all users that are in the current shop's area
        area_users = {
            uid: prof for (uid, prof) in self.data.local.user.get_all_profiles(self.game, self.version)
            if prof.get_int('area', 51) == shop_area
        }

        # Second, look up records belonging only to those users
        records = self.data.local.music.get_all_records(self.game, self.music_version, userlist=list(area_users.keys()))

        # Now, do the same lazy thing as 'hiscore' because I don't want
        # to think about how to change this knowing that we only pulled
        # up area records.
        area_records: Dict[int, Dict[int, Tuple[UserID, Score]]] = {}
        for (userid, score) in records:
            if score.id not in area_records:
                area_records[score.id] = {}
            area_records[score.id][score.chart] = (userid, score)

        game = Node.void('game')
        for song in area_records:
            music = Node.void('music')
            game.add_child(music)
            music.set_attribute('reclink_num', str(song))

            for chart in area_records[song]:
                userid, score = area_records[song][chart]
                if area_users[userid].get_int('area', 51) != shop_area:
                    # Don't return this, this user isn't in this area
                    continue
                try:
                    gamechart = self.db_to_game_chart(chart)
                except KeyError:
                    # Don't support this chart in this game
                    continue
                gamerank = self.db_to_game_rank(score.data.get_int('rank'))
                combo_type = self.db_to_game_halo(score.data.get_int('halo'))

                typenode = Node.void('type')
                music.add_child(typenode)
                typenode.set_attribute('diff', str(gamechart))

                typenode.add_child(Node.string('name', area_users[userid].get_str('name')))
                typenode.add_child(Node.u32('score', score.points))
                typenode.add_child(Node.u16('area', area_users[userid].get_int('area', 51)))
                typenode.add_child(Node.u8('rank', gamerank))
                typenode.add_child(Node.u8('combo_type', combo_type))
                typenode.add_child(Node.u32('code', area_users[userid].get_int('extid')))

        return game
Exemple #18
0
    def handle_pcb22_request(self, request: Node) -> Optional[Node]:
        method = request.attribute('method')

        if method == 'boot':
            return Node.void('pcb22')
        elif method == 'error':
            return Node.void('pcb22')
        elif method == 'write':
            # Update the name of this cab for admin purposes
            self.update_machine_name(request.child_value('pcb_setting/name'))
            return Node.void('pcb22')

        # Invalid method
        return None
Exemple #19
0
    def handle_game_load_m_request(self, request: Node) -> Node:
        refid = request.attribute('dataid')

        if refid is not None:
            userid = self.data.remote.user.from_refid(self.game, self.version,
                                                      refid)
        else:
            userid = None

        if userid is not None:
            scores = self.data.remote.music.get_scores(self.game, self.version,
                                                       userid)
        else:
            scores = []

        # Organize by song->chart
        scores_by_id: Dict[int, Dict[int, Score]] = {}
        for score in scores:
            if score.id not in scores_by_id:
                scores_by_id[score.id] = {}

            scores_by_id[score.id][score.chart] = score

        # Output to the game
        game = Node.void('game')
        for songid in scores_by_id:
            music = Node.void('music')
            game.add_child(music)
            music.set_attribute('music_id', str(songid))

            for chart in scores_by_id[songid]:
                typenode = Node.void('type')
                music.add_child(typenode)
                typenode.set_attribute('type_id', str(chart))

                score = scores_by_id[songid][chart]
                typenode.set_attribute('score', str(score.points))
                typenode.set_attribute('cnt', str(score.plays))
                typenode.set_attribute(
                    'clear_type',
                    str(
                        self.__db_to_game_clear_type(
                            score.data.get_int('clear_type'))))
                typenode.set_attribute(
                    'score_grade',
                    str(self.__db_to_game_grade(score.data.get_int('grade'))))

        return game
Exemple #20
0
    def handle_game_old_request(self, request: Node) -> Node:
        refid = request.attribute('refid')
        game = Node.void('game')

        userid = self.data.remote.user.from_refid(self.game, self.version, refid)
        previous_version: Optional[DDRBase] = None
        oldprofile: Optional[ValidatedDict] = None

        if userid is not None:
            previous_version = self.previous_version()
        if previous_version is not None:
            oldprofile = previous_version.get_profile(userid)
        if oldprofile is not None:
            game.set_attribute('name', oldprofile.get_str('name'))
            game.set_attribute('area', str(oldprofile.get_int('area', 51)))
        return game
Exemple #21
0
    def handle_playerdata_request(self, request: Node) -> Optional[Node]:
        method = request.attribute('method')

        if method == 'expire':
            return Node.void('playerdata')

        elif method == 'logout':
            return Node.void('playerdata')

        elif method == 'get':
            modelstring = request.attribute('model')
            refid = request.attribute('ref_id')
            root = self.get_profile_by_refid(
                refid,
                self.NEW_PROFILE_ONLY
                if modelstring is None else self.OLD_PROFILE_ONLY,
            )
            if root is None:
                root = Node.void('playerdata')
                root.set_attribute('status', str(Status.NO_PROFILE))
            return root

        elif method == 'new':
            refid = request.attribute('ref_id')
            name = request.attribute('name')
            root = self.new_profile_by_refid(refid, name)
            if root is None:
                root = Node.void('playerdata')
                root.set_attribute('status', str(Status.NO_PROFILE))
            return root

        elif method == 'set':
            refid = request.attribute('ref_id')

            root = Node.void('playerdata')
            if refid is None:
                return root

            userid = self.data.remote.user.from_refid(self.game, self.version,
                                                      refid)
            if userid is None:
                return root

            oldprofile = self.get_profile(userid) or ValidatedDict()
            newprofile = self.unformat_profile(userid, request, oldprofile)

            if newprofile is not None:
                self.put_profile(userid, newprofile)

            return root

        # Invalid method
        return None
Exemple #22
0
    def handle_game_save_request(self, request: Node) -> Node:
        refid = request.attribute('refid')

        if refid is not None:
            userid = self.data.remote.user.from_refid(self.game, self.version, refid)
        else:
            userid = None

        if userid is not None:
            oldprofile = self.get_profile(userid)
            newprofile = self.unformat_profile(userid, request, oldprofile)
        else:
            newprofile = None

        if userid is not None and newprofile is not None:
            self.put_profile(userid, newprofile)

        return Node.void('game')
Exemple #23
0
    def handle_info22_request(self, request: Node) -> Optional[Node]:
        method = request.attribute('method')

        if method == 'common':
            # TODO: Hook these up to config so we can change this
            phases = {
                # Unknown event
                0: 0,
                # Unknown event
                1: 0,
                # Pop'n Aura, max 10 (remov all aura requirements)
                2: 10,
                # Story
                3: 1,
                # BEMANI ruins Discovery!
                4: 0,
                # Unknown event
                5: 0,
                # Unknown event
                6: 0,
                # Unknown event
                7: 0,
                # Unknown event
                8: 0,
                # Unknown event
                9: 0,
                # Unknown event
                10: 0,
                # Unknown event
                11: 0,
                # Unknown event
                12: 0,
                # Unknown event
                13: 0,
                # Unknown event
                14: 0,
                # Unknown event
                15: 0,
                # Unknown event
                16: 0,
                # Unknown event
                17: 0,
                # Unknown event
                18: 0,
                # Unknown event
                19: 0,
            }
            stories = list(range(173))

            root = Node.void('info22')
            for phaseid in phases:
                phase = Node.void('phase')
                root.add_child(phase)
                phase.add_child(Node.s16('event_id', phaseid))
                phase.add_child(Node.s16('phase', phases[phaseid]))

            for storyid in stories:
                story = Node.void('story')
                root.add_child(story)
                story.add_child(Node.u32('story_id', storyid))
                story.add_child(Node.bool('is_limited', False))
                story.add_child(Node.u64('limit_date', 0))

            return root

        # Invalid method
        return None
Exemple #24
0
    def handle_game_friend_request(self, request: Node) -> Node:
        extid = intish(request.attribute('code'))
        userid = None
        friend = None

        if extid is not None:
            # Rival score loading
            userid = self.data.remote.user.from_extid(self.game, self.version, extid)
        if userid is not None:
            friend = self.get_profile(userid)
            play_stats = self.get_play_statistics(userid)

        if friend is None:
            # Return an empty node to tell the game we don't have a player here
            game = Node.void('game')
            return game

        game = Node.void('game')
        game.set_attribute('data', '1')
        game.add_child(Node.u32('code', friend.get_int('extid')))
        game.add_child(Node.string('name', friend.get_str('name')))
        game.add_child(Node.u8('area', friend.get_int('area', 51)))
        game.add_child(Node.u32('exp', play_stats.get_int('exp')))
        game.add_child(Node.u32('star', friend.get_int('star')))

        # Drill rankings
        if 'title' in friend:
            title = Node.void('title')
            game.add_child(title)
            titledict = friend.get_dict('title')
            if 't' in titledict:
                title.set_attribute('t', str(titledict.get_int('t')))
            if 's' in titledict:
                title.set_attribute('s', str(titledict.get_int('s')))
            if 'd' in titledict:
                title.set_attribute('d', str(titledict.get_int('d')))

        if 'title_gr' in friend:
            title_gr = Node.void('title_gr')
            game.add_child(title_gr)
            title_grdict = friend.get_dict('title_gr')
            if 't' in title_grdict:
                title_gr.set_attribute('t', str(title_grdict.get_int('t')))
            if 's' in title_grdict:
                title_gr.set_attribute('s', str(title_grdict.get_int('s')))
            if 'd' in title_grdict:
                title_gr.set_attribute('d', str(title_grdict.get_int('d')))

        # Groove gauge level-ups
        gr_s = Node.void('gr_s')
        game.add_child(gr_s)
        index = 1
        for entry in friend.get_int_array('gr_s', 5):
            gr_s.set_attribute(f'gr{index}', str(entry))
            index = index + 1

        gr_d = Node.void('gr_d')
        game.add_child(gr_d)
        index = 1
        for entry in friend.get_int_array('gr_d', 5):
            gr_d.set_attribute(f'gr{index}', str(entry))
            index = index + 1
        return game
Exemple #25
0
    def handle_player22_request(self, request: Node) -> Optional[Node]:
        method = request.attribute('method')

        if method == 'read':
            refid = request.child_value('ref_id')
            # Pop'n Music 22 doesn't send a modelstring to load old profiles,
            # it just expects us to know. So always look for old profiles in
            # Pop'n 22 land.
            root = self.get_profile_by_refid(refid,
                                             self.OLD_PROFILE_FALLTHROUGH)
            if root is None:
                root = Node.void('player22')
                root.set_attribute('status', str(Status.NO_PROFILE))
            return root

        elif method == 'new':
            refid = request.child_value('ref_id')
            name = request.child_value('name')
            root = self.new_profile_by_refid(refid, name)
            if root is None:
                root = Node.void('player22')
                root.set_attribute('status', str(Status.NO_PROFILE))
            return root

        elif method == 'start':
            return Node.void('player22')

        elif method == 'logout':
            return Node.void('player22')

        elif method == 'write':
            refid = request.child_value('ref_id')

            root = Node.void('player22')
            if refid is None:
                return root

            userid = self.data.remote.user.from_refid(self.game, self.version,
                                                      refid)
            if userid is None:
                return root

            oldprofile = self.get_profile(userid) or ValidatedDict()
            newprofile = self.unformat_profile(userid, request, oldprofile)

            if newprofile is not None:
                self.put_profile(userid, newprofile)

            return root

        elif method == 'conversion':
            refid = request.child_value('ref_id')
            name = request.child_value('name')
            chara = request.child_value('chara')
            root = self.new_profile_by_refid(refid, name, chara)
            if root is None:
                root = Node.void('playerdata')
                root.set_attribute('status', str(Status.NO_PROFILE))
            return root

        elif method == 'write_music':
            refid = request.child_value('ref_id')

            root = Node.void('player22')
            if refid is None:
                return root

            userid = self.data.remote.user.from_refid(self.game, self.version,
                                                      refid)
            if userid is None:
                return root

            songid = request.child_value('music_num')
            chart = {
                self.GAME_CHART_TYPE_EASY: self.CHART_TYPE_EASY,
                self.GAME_CHART_TYPE_NORMAL: self.CHART_TYPE_NORMAL,
                self.GAME_CHART_TYPE_HYPER: self.CHART_TYPE_HYPER,
                self.GAME_CHART_TYPE_EX: self.CHART_TYPE_EX,
            }[request.child_value('sheet_num')]
            medal = request.child_value('clearmedal')
            points = request.child_value('score')
            combo = request.child_value('combo')
            stats = {
                'cool': request.child_value('cool'),
                'great': request.child_value('great'),
                'good': request.child_value('good'),
                'bad': request.child_value('bad')
            }
            medal = {
                self.GAME_PLAY_MEDAL_CIRCLE_FAILED:
                self.PLAY_MEDAL_CIRCLE_FAILED,
                self.GAME_PLAY_MEDAL_DIAMOND_FAILED:
                self.PLAY_MEDAL_DIAMOND_FAILED,
                self.GAME_PLAY_MEDAL_STAR_FAILED: self.PLAY_MEDAL_STAR_FAILED,
                self.GAME_PLAY_MEDAL_EASY_CLEAR: self.PLAY_MEDAL_EASY_CLEAR,
                self.GAME_PLAY_MEDAL_CIRCLE_CLEARED:
                self.PLAY_MEDAL_CIRCLE_CLEARED,
                self.GAME_PLAY_MEDAL_DIAMOND_CLEARED:
                self.PLAY_MEDAL_DIAMOND_CLEARED,
                self.GAME_PLAY_MEDAL_STAR_CLEARED:
                self.PLAY_MEDAL_STAR_CLEARED,
                self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO:
                self.PLAY_MEDAL_CIRCLE_FULL_COMBO,
                self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO:
                self.PLAY_MEDAL_DIAMOND_FULL_COMBO,
                self.GAME_PLAY_MEDAL_STAR_FULL_COMBO:
                self.PLAY_MEDAL_STAR_FULL_COMBO,
                self.GAME_PLAY_MEDAL_PERFECT: self.PLAY_MEDAL_PERFECT,
            }[medal]
            self.update_score(userid,
                              songid,
                              chart,
                              points,
                              medal,
                              combo=combo,
                              stats=stats)
            return root

        # Invalid method
        return None
Exemple #26
0
    def unformat_profile(self, userid: UserID, request: Node, oldprofile: ValidatedDict) -> ValidatedDict:
        newprofile = copy.deepcopy(oldprofile)
        play_stats = self.get_play_statistics(userid)

        # Grab last node and accessories so we can make decisions based on type
        last = request.child('last')
        lastdict = newprofile.get_dict('last')
        mode = int(last.attribute('mode'))
        style = int(last.attribute('style'))
        is_dp = style == self.GAME_STYLE_DOUBLE

        # Drill rankings
        title = request.child('title')
        title_gr = request.child('title_gr')
        titledict = newprofile.get_dict('title')
        title_grdict = newprofile.get_dict('title_gr')

        # Groove radar level ups
        gr = request.child('gr')

        # Set the correct values depending on if we're single or double play
        if is_dp:
            play_stats.increment_int('double_plays')
            if gr is not None:
                newprofile.replace_int_array(
                    'gr_d',
                    5,
                    [
                        intish(gr.attribute('gr1')),
                        intish(gr.attribute('gr2')),
                        intish(gr.attribute('gr3')),
                        intish(gr.attribute('gr4')),
                        intish(gr.attribute('gr5')),
                    ],
                )
            if title is not None:
                titledict.replace_int('d', title.value)
                newprofile.replace_dict('title', titledict)
            if title_gr is not None:
                title_grdict.replace_int('d', title.value)
                newprofile.replace_dict('title_gr', title_grdict)
        else:
            play_stats.increment_int('single_plays')
            if gr is not None:
                newprofile.replace_int_array(
                    'gr_s',
                    5,
                    [
                        intish(gr.attribute('gr1')),
                        intish(gr.attribute('gr2')),
                        intish(gr.attribute('gr3')),
                        intish(gr.attribute('gr4')),
                        intish(gr.attribute('gr5')),
                    ],
                )
            if title is not None:
                titledict.replace_int('s', title.value)
                newprofile.replace_dict('title', titledict)
            if title_gr is not None:
                title_grdict.replace_int('s', title.value)
                newprofile.replace_dict('title_gr', title_grdict)
        play_stats.increment_int(f'cnt_m{mode}')

        # Result stars
        result_star = request.child('result_star')
        if result_star is not None:
            newprofile.replace_int_array(
                'result_stars',
                9,
                [
                    intish(result_star.attribute('slot1')),
                    intish(result_star.attribute('slot2')),
                    intish(result_star.attribute('slot3')),
                    intish(result_star.attribute('slot4')),
                    intish(result_star.attribute('slot5')),
                    intish(result_star.attribute('slot6')),
                    intish(result_star.attribute('slot7')),
                    intish(result_star.attribute('slot8')),
                    intish(result_star.attribute('slot9')),
                ],
            )

        # Target stuff
        target = request.child('target')
        if target is not None:
            newprofile.replace_int('target_flag', intish(target.attribute('flag')))
            newprofile.replace_int('target_setnum', intish(target.attribute('setnum')))

        # Update last attributes
        lastdict.replace_int('rival1', intish(last.attribute('rival1')))
        lastdict.replace_int('rival2', intish(last.attribute('rival2')))
        lastdict.replace_int('rival3', intish(last.attribute('rival3')))
        lastdict.replace_int('style', intish(last.attribute('style')))
        lastdict.replace_int('mode', intish(last.attribute('mode')))
        lastdict.replace_int('cate', intish(last.attribute('cate')))
        lastdict.replace_int('sort', intish(last.attribute('sort')))
        lastdict.replace_int('mid', intish(last.attribute('mid')))
        lastdict.replace_int('mtype', intish(last.attribute('mtype')))
        lastdict.replace_int('cid', intish(last.attribute('cid')))
        lastdict.replace_int('ctype', intish(last.attribute('ctype')))
        lastdict.replace_int('sid', intish(last.attribute('sid')))
        newprofile.replace_dict('last', lastdict)

        # Grab character options
        chara = request.child('chara')
        if chara is not None:
            newprofile.replace_int('chara', intish(chara.attribute('my')))
        chara_opt = request.child('chara_opt')
        if chara_opt is not None:
            # A bug in old versions of AVS returns the wrong number for set
            newprofile.replace_int_array('chara_opt', 96, chara_opt.value[:96])

        # Options
        opt = request.child('opt')
        if opt is not None:
            # A bug in old versions of AVS returns the wrong number for set
            newprofile.replace_int_array('opt', 16, opt.value[:16])

        # Experience and stars
        exp = request.child_value('exp')
        if exp is not None:
            play_stats.replace_int('exp', play_stats.get_int('exp') + exp)
        star = request.child_value('star')
        if star is not None:
            newprofile.replace_int('star', newprofile.get_int('star') + star)
        star_c = request.child_value('star_c')
        if star_c is not None:
            newprofile.replace_int('star_c', newprofile.get_int('star_c') + exp)

        # Update game flags
        for child in request.children:
            if child.name != 'flag':
                continue
            try:
                value = int(child.attribute('data'))
                offset = int(child.attribute('no'))
            except ValueError:
                continue

            flags = newprofile.get_int_array('flag', 256, [1] * 256)
            if offset < 0 or offset >= len(flags):
                continue
            flags[offset] = value
            newprofile.replace_int_array('flag', 256, flags)

        # Workout mode support
        newweight = -1
        oldweight = newprofile.get_int('weight')
        for child in request.children:
            if child.name != 'weight':
                continue
            newweight = child.value
        if newweight < 0:
            newweight = oldweight

        # Either update or unset the weight depending on the game
        if newweight == 0:
            # Weight is unset or we declined to use this feature, remove from profile
            if 'weight' in newprofile:
                del newprofile['weight']
        else:
            # Weight has been set or previously retrieved, we should save calories
            newprofile.replace_int('weight', newweight)
            total = 0
            for child in request.children:
                if child.name != 'calory':
                    continue
                total += child.value
            self.data.local.user.put_time_based_achievement(
                self.game,
                self.version,
                userid,
                0,
                'workout',
                {
                    'calories': total,
                    'weight': newweight,
                },
            )

        # Look up old friends
        oldfriends: List[Optional[UserID]] = [None] * 10
        links = self.data.local.user.get_links(self.game, self.version, userid)
        for link in links:
            if link.type[:7] != 'friend_':
                continue

            pos = int(link.type[7:])
            oldfriends[pos] = link.other_userid

        # Save any rivals that were added/removed/changed
        newfriends = oldfriends[:]
        for child in request.children:
            if child.name != 'friend':
                continue

            code = int(child.attribute('code'))
            pos = int(child.attribute('pos'))

            if pos >= 0 and pos < 10:
                if code == 0:
                    # We cleared this friend
                    newfriends[pos] = None
                else:
                    # Try looking up the userid
                    newfriends[pos] = self.data.remote.user.from_extid(self.game, self.version, code)

        # Diff the set of links to determine updates
        for i in range(10):
            if newfriends[i] == oldfriends[i]:
                continue

            if newfriends[i] is None:
                # Kill the rival in this location
                self.data.local.user.destroy_link(
                    self.game,
                    self.version,
                    userid,
                    f'friend_{i}',
                    oldfriends[i],
                )
            elif oldfriends[i] is None:
                # Add rival in this location
                self.data.local.user.put_link(
                    self.game,
                    self.version,
                    userid,
                    f'friend_{i}',
                    newfriends[i],
                    {},
                )
            else:
                # Changed the rival here, kill the old one, add the new one
                self.data.local.user.destroy_link(
                    self.game,
                    self.version,
                    userid,
                    f'friend_{i}',
                    oldfriends[i],
                )
                self.data.local.user.put_link(
                    self.game,
                    self.version,
                    userid,
                    f'friend_{i}',
                    newfriends[i],
                    {},
                )

        # Play area counter
        shop_area = int(request.attribute('shop_area'))
        if shop_area >= 0 and shop_area < 55:
            areas = newprofile.get_int_array('play_area', 55)
            areas[shop_area] = areas[shop_area] + 1
            newprofile.replace_int_array('play_area', 55, areas)

        # Keep track of play statistics
        self.update_play_statistics(userid, play_stats)

        return newprofile
Exemple #27
0
    def handle_game_save_m_request(self, request: Node) -> Node:
        refid = request.attribute('refid')
        songid = int(request.attribute('mid'))
        chart = self.game_to_db_chart(int(request.attribute('mtype')))
        userid = self.data.remote.user.from_refid(self.game, self.version, refid)

        # Calculate statistics
        data = request.child('data')
        playmode = int(data.attribute('playmode'))
        if playmode == self.GAME_PLAY_MODE_2NDMIX:
            if userid is not None:
                # These are special cased and treated like achievements
                points = int(data.attribute('score_2nd'))
                combo = int(data.attribute('combo_2nd'))
                rank = int(data.attribute('rank_2nd'))

                # Grab the old 2nd mix score
                existingscore = self.data.local.user.get_achievement(
                    self.game,
                    self.music_version,
                    userid,
                    (songid * 100) + chart,
                    '2ndmix',
                )

                if existingscore is not None:
                    highscore = points > existingscore.get_int('points')

                    plays = existingscore.get_int('plays', 0) + 1
                    points = max(points, existingscore.get_int('points'))
                    if not highscore:
                        combo = existingscore.get_int('combo', combo)
                        rank = existingscore.get_int('rank', rank)
                else:
                    plays = 1

                self.data.local.user.put_achievement(
                    self.game,
                    self.music_version,
                    userid,
                    (songid * 100) + chart,
                    '2ndmix',
                    {
                        'points': points,
                        'combo': combo,
                        'rank': rank,
                        'plays': plays,
                    },
                )
        else:
            points = int(data.attribute('score'))
            combo = int(data.attribute('combo'))
            rank = self.game_to_db_rank(int(data.attribute('rank')))
            if int(data.attribute('full')) == 0:
                halo = self.HALO_NONE
            elif int(data.attribute('perf')) == 0:
                halo = self.HALO_GREAT_FULL_COMBO
            elif points < 1000000:
                halo = self.HALO_PERFECT_FULL_COMBO
            else:
                halo = self.HALO_MARVELOUS_FULL_COMBO
            trace = request.child_value('trace')

            # Save the score, regardless of whether we have a refid. If we save
            # an anonymous score, it only goes into the DB to count against the
            # number of plays for that song/chart.
            self.update_score(
                userid,
                songid,
                chart,
                points,
                rank,
                halo,
                combo,
                trace,
            )

        # No response needed
        game = Node.void('game')
        return game
Exemple #28
0
    def handle_game_load_m_request(self, request: Node) -> Node:
        extid = intish(request.attribute('code'))
        refid = request.attribute('refid')

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

        if userid is not None:
            scores = self.data.remote.music.get_scores(self.game, self.music_version, userid)
            old_scores = [
                score for score in self.data.local.user.get_achievements(self.game, self.music_version, userid)
                if score.type == '2ndmix'
            ]
        else:
            scores = []
            old_scores = []

        sortedscores: Dict[int, Dict[int, Dict[str, Union[Score, Achievement]]]] = {}

        for score in scores:
            if score.id not in sortedscores:
                sortedscores[score.id] = {}
            if score.chart not in sortedscores[score.id]:
                sortedscores[score.id][score.chart] = {}
            sortedscores[score.id][score.chart]['score'] = score

        for oldscore in old_scores:
            songid = int(oldscore.id / 100)
            chart = int(oldscore.id % 100)
            if songid not in sortedscores:
                sortedscores[songid] = {}
            if chart not in sortedscores[songid]:
                sortedscores[songid][chart] = {}
            sortedscores[songid][chart]['oldscore'] = oldscore

        game = Node.void('game')
        for song in sortedscores:
            music = Node.void('music')
            game.add_child(music)
            music.set_attribute('reclink', str(song))

            for chart in sortedscores[song]:
                try:
                    gamechart = self.db_to_game_chart(chart)
                except KeyError:
                    # Don't support this chart in this game
                    continue
                scoredict = sortedscores[song][chart]

                if 'score' in scoredict:
                    # We played the normal version of this song
                    gamerank = self.db_to_game_rank(scoredict['score'].data.get_int('rank'))
                    combo_type = self.db_to_game_halo(scoredict['score'].data.get_int('halo'))
                    points = scoredict['score'].points  # type: ignore
                    plays = scoredict['score'].plays  # type: ignore
                else:
                    # We only played 2nd mix version of this song
                    gamerank = 0
                    combo_type = self.GAME_HALO_NONE
                    points = 0
                    plays = 0

                if 'oldscore' in scoredict:
                    # We played the 2nd mix version of this song
                    oldpoints = scoredict['oldscore'].data.get_int('points')
                    oldrank = scoredict['oldscore'].data.get_int('rank')
                    oldplays = scoredict['oldscore'].data.get_int('plays')
                else:
                    oldpoints = 0
                    oldrank = 0
                    oldplays = 0

                typenode = Node.void('type')
                music.add_child(typenode)
                typenode.set_attribute('diff', str(gamechart))

                typenode.add_child(Node.u32('score', points))
                typenode.add_child(Node.u16('count', plays))
                typenode.add_child(Node.u8('rank', gamerank))
                typenode.add_child(Node.u8('combo_type', combo_type))
                typenode.add_child(Node.u32('score_2nd', oldpoints))
                typenode.add_child(Node.u8('rank_2nd', oldrank))
                typenode.add_child(Node.u16('cnt_2nd', oldplays))

        return game
Exemple #29
0
    def handle_eacoin_request(self, request: Node) -> Optional[Node]:
        """
        Handle PASELI requests. The game will check out a session at the beginning
        of the game, make PASELI purchases against that session, and then close it
        ad the end of of a game. This handler ensures that this works for all games.
        """
        method = request.attribute('method')

        if not self.config['paseli']['enabled']:
            # Refuse to respond, we don't have PASELI enabled
            print("PASELI not enabled, ignoring eacoin request")
            root = Node.void('eacoin')
            root.set_attribute('status', str(Status.NOT_ALLOWED))
            return root

        if method == 'checkin':
            root = Node.void('eacoin')
            cardid = request.child_value('cardid')
            pin = request.child_value('passwd')

            if cardid is None or pin is None:
                # Refuse to return anything
                print("Invalid eacoin checkin request, missing cardid or pin")
                root.set_attribute('status', str(Status.NO_PROFILE))
                return root

            userid = self.data.local.user.from_cardid(cardid)
            if userid is None:
                # Refuse to do anything
                print("No user for eacoin checkin request")
                root.set_attribute('status', str(Status.NO_PROFILE))
                return root

            valid = self.data.local.user.validate_pin(userid, pin)
            if not valid:
                # Refuse to do anything
                print("User entered invalid pin for eacoin checkin request")
                root.set_attribute('status', str(Status.INVALID_PIN))
                return root

            session = self.data.local.user.create_session(userid)

            if self.config['paseli']['infinite']:
                balance = PASELIHandler.INFINITE_PASELI_AMOUNT
            else:
                if self.config['machine']['arcade'] is None:
                    # There's no arcade for this machine, but infinite is not
                    # enabled, so there's no way to find a balance.
                    balance = 0
                else:
                    balance = self.data.local.user.get_balance(userid, self.config['machine']['arcade'])

            root.add_child(Node.s16('sequence', 0))
            root.add_child(Node.u8('acstatus', 0))
            root.add_child(Node.string('acid', 'DUMMY_ID'))
            root.add_child(Node.string('acname', 'DUMMY_NAME'))
            root.add_child(Node.s32('balance', balance))
            root.add_child(Node.string('sessid', session))
            return root

        if method == 'opcheckin':
            root = Node.void('eacoin')
            passwd = request.child_value('passwd')

            if passwd is None:
                # Refuse to return anything
                print("Invalid eacoin checkin request, missing passwd")
                root.set_attribute('status', str(Status.NO_PROFILE))
                return root

            if self.config['machine']['arcade'] is None:
                # Machine doesn't belong to an arcade
                print("Machine doesn't belong to an arcade")
                root.set_attribute('status', str(Status.NO_PROFILE))
                return root

            arcade = self.data.local.machine.get_arcade(self.config['machine']['arcade'])
            if arcade is None:
                # Refuse to do anything
                print("No arcade for operator checkin request")
                root.set_attribute('status', str(Status.NO_PROFILE))
                return root

            if arcade.pin != passwd:
                # Refuse to do anything
                print("User entered invalid pin for operator checkin request")
                root.set_attribute('status', str(Status.INVALID_PIN))
                return root

            session = self.data.local.machine.create_session(arcade.id)
            root.add_child(Node.string('sessid', session))
            return root

        elif method == 'consume':

            def make_resp(status: int, balance: int) -> Node:
                root = Node.void('eacoin')
                root.add_child(Node.u8('acstatus', status))
                root.add_child(Node.u8('autocharge', 0))
                root.add_child(Node.s32('balance', balance))
                return root

            session = request.child_value('sessid')
            payment = request.child_value('payment')
            service = request.child_value('service')
            details = request.child_value('detail')
            if session is None or payment is None:
                # Refuse to do anything
                print("Invalid eacoin consume request, missing sessid or payment")
                return make_resp(2, 0)

            userid = self.data.local.user.from_session(session)
            if userid is None:
                # Refuse to do anything
                print("Invalid session for eacoin consume request")
                return make_resp(2, 0)

            if self.config['paseli']['infinite']:
                balance = PASELIHandler.INFINITE_PASELI_AMOUNT - payment
            else:
                if self.config['machine']['arcade'] is None:
                    # There's no arcade for this machine, but infinite is not
                    # enabled, so there's no way to find a balance, assume failed
                    # consume payment.
                    balance = None
                else:
                    # Look up the new balance based on this delta. If there isn't enough,
                    # we will end up returning None here and exit without performing.
                    balance = self.data.local.user.update_balance(userid, self.config['machine']['arcade'], -payment)

                if balance is None:
                    print("Not enough balance for eacoin consume request")
                    return make_resp(1, self.data.local.user.get_balance(userid, self.config['machine']['arcade']))
                else:
                    self.data.local.network.put_event(
                        'paseli_transaction',
                        {
                            'delta': -payment,
                            'balance': balance,
                            'service': -service,
                            'reason': details,
                            'pcbid': self.config['machine']['pcbid'],
                        },
                        userid=userid,
                        arcadeid=self.config['machine']['arcade'],
                    )

            return make_resp(0, balance)

        elif method == 'getlog':
            root = Node.void('eacoin')
            sessid = request.child_value('sessid')
            logtype = request.child_value('logtype')
            target = request.child_value('target')
            limit = request.child_value('perpage')
            offset = request.child_value('offset')

            # Try to determine whether its a user or an arcade session
            userid = self.data.local.user.from_session(sessid)
            if userid is None:
                arcadeid = self.data.local.machine.from_session(sessid)
            else:
                arcadeid = None

            # Bail out if we don't have any idea what session this is
            if userid is None and arcadeid is None:
                print("Unable to determine session type")
                return root

            # If we're a user session, also look up the current arcade
            # so we display only entries that happened on this arcade.
            if userid is not None:
                arcade = self.data.local.machine.get_arcade(self.config['machine']['arcade'])
                if arcade is None:
                    print("Machine doesn't belong to an arcade")
                    return root
                arcadeid = arcade.id

            # Now, look up all transactions for this specific group
            events = self.data.local.network.get_events(
                userid=userid,
                arcadeid=arcadeid,
                event='paseli_transaction',
            )

            # Further filter it down to the current PCBID
            events = [event for event in events if event.data.get('pcbid') == target]

            # Grab the end of day today as a timestamp
            end_of_today = Time.end_of_today()
            time_format = '%Y-%m-%d %H:%M:%S'
            date_format = '%Y-%m-%d'

            # Set up common structure
            lognode = Node.void(logtype)
            topic = Node.void('topic')
            lognode.add_child(topic)
            summary = Node.void('summary')
            lognode.add_child(summary)

            # Display what day we are summed to
            topic.add_child(Node.string('sumdate', Time.format(Time.now(), date_format)))

            if logtype == 'last7days':
                # We show today in the today total, last 7 days prior in the week total
                beginning_of_today = end_of_today - Time.SECONDS_IN_DAY
                end_of_week = beginning_of_today
                beginning_of_week = end_of_week - Time.SECONDS_IN_WEEK

                topic.add_child(Node.string('sumfrom', Time.format(beginning_of_week, date_format)))
                topic.add_child(Node.string('sumto', Time.format(end_of_week, date_format)))
                today_total = sum([
                    -event.data.get_int('delta') for event in events
                    if event.timestamp >= beginning_of_today and event.timestamp < end_of_today
                ])

                today_total = sum([
                    -event.data.get_int('delta') for event in events
                    if event.timestamp >= beginning_of_today and event.timestamp < end_of_today
                ])
                week_txns = [
                    -event.data.get_int('delta') for event in events
                    if event.timestamp >= beginning_of_week and event.timestamp < end_of_week
                ]
                week_total = sum(week_txns)
                if len(week_txns) > 0:
                    week_avg = int(sum(week_txns) / len(week_txns))
                else:
                    week_avg = 0

                # We display the totals for each day starting with yesterday and up through 7 days prior.
                # Index starts at 0 = yesterday, 1 = the day before, etc...
                items = []
                for days in range(0, 7):
                    end_of_day = end_of_week - (days * Time.SECONDS_IN_DAY)
                    start_of_day = end_of_day - Time.SECONDS_IN_DAY

                    items.append(sum([
                        -event.data.get_int('delta') for event in events
                        if event.timestamp >= start_of_day and event.timestamp < end_of_day
                    ]))

                topic.add_child(Node.s32('today', today_total))
                topic.add_child(Node.s32('average', week_avg))
                topic.add_child(Node.s32('total', week_total))
                summary.add_child(Node.s32_array('items', items))

            if logtype == 'last52weeks':
                # Start one week back, since the operator can look at last7days for newer stuff.
                beginning_of_today = end_of_today - Time.SECONDS_IN_DAY
                end_of_52_weeks = beginning_of_today - Time.SECONDS_IN_WEEK

                topic.add_child(Node.string('sumfrom', Time.format(end_of_52_weeks - (52 * Time.SECONDS_IN_WEEK), date_format)))
                topic.add_child(Node.string('sumto', Time.format(end_of_52_weeks, date_format)))

                # We index backwards, where index 0 = the first week back, 1 = the next week back after that, etc...
                items = []
                for weeks in range(0, 52):
                    end_of_range = end_of_52_weeks - (weeks * Time.SECONDS_IN_WEEK)
                    beginning_of_range = end_of_range - Time.SECONDS_IN_WEEK

                    items.append(sum([
                        -event.data.get_int('delta') for event in events
                        if event.timestamp >= beginning_of_range and event.timestamp < end_of_range
                    ]))

                summary.add_child(Node.s32_array('items', items))

            if logtype == 'eachday':
                start_ts = Time.now()
                end_ts = Time.now()
                weekdays = [0] * 7

                for event in events:
                    event_day = Time.days_into_week(event.timestamp)
                    weekdays[event_day] = weekdays[event_day] - event.data.get_int('delta')
                    if event.timestamp < start_ts:
                        start_ts = event.timestamp

                topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format)))
                topic.add_child(Node.string('sumto', Time.format(end_ts, date_format)))
                summary.add_child(Node.s32_array('items', weekdays))

            if logtype == 'eachhour':
                start_ts = Time.now()
                end_ts = Time.now()
                hours = [0] * 24

                for event in events:
                    event_hour = int((event.timestamp % Time.SECONDS_IN_DAY) / Time.SECONDS_IN_HOUR)
                    hours[event_hour] = hours[event_hour] - event.data.get_int('delta')
                    if event.timestamp < start_ts:
                        start_ts = event.timestamp

                topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format)))
                topic.add_child(Node.string('sumto', Time.format(end_ts, date_format)))
                summary.add_child(Node.s32_array('items', hours))

            if logtype == 'detail':
                history = Node.void('history')
                lognode.add_child(history)

                # Respect details paging
                if offset is not None:
                    events = events[offset:]
                if limit is not None:
                    events = events[:limit]

                # Output the details themselves
                for event in events:
                    card_no = ''
                    if event.userid is not None:
                        user = self.data.local.user.get_user(event.userid)
                        if user is not None:
                            cards = self.data.local.user.get_cards(user.id)
                            if len(cards) > 0:
                                card_no = CardCipher.encode(cards[0])

                    item = Node.void('item')
                    history.add_child(item)
                    item.add_child(Node.string('date', Time.format(event.timestamp, time_format)))
                    item.add_child(Node.s32('consume', -event.data.get_int('delta')))
                    item.add_child(Node.s32('service', -event.data.get_int('service')))
                    item.add_child(Node.string('cardtype', ''))
                    item.add_child(Node.string('cardno', ' ' * self.paseli_padding + card_no))
                    item.add_child(Node.string('title', ''))
                    item.add_child(Node.string('systemid', ''))

            if logtype == 'lastmonths':
                year, month, _ = Time.todays_date()
                this_month = Time.timestamp_from_date(year, month)
                last_month = Time.timestamp_from_date(year, month - 1)
                month_before = Time.timestamp_from_date(year, month - 2)

                topic.add_child(Node.string('sumfrom', Time.format(month_before, date_format)))
                topic.add_child(Node.string('sumto', Time.format(this_month, date_format)))

                for (start, end) in [(month_before, last_month), (last_month, this_month)]:
                    year, month, _ = Time.date_from_timestamp(start)

                    items = []
                    for day in range(0, 31):
                        begin_ts = start + (day * Time.SECONDS_IN_DAY)
                        end_ts = begin_ts + Time.SECONDS_IN_DAY
                        if begin_ts >= end:
                            # Passed the end of this month
                            items.append(0)
                        else:
                            # Sum up all the txns for this day
                            items.append(sum([
                                -event.data.get_int('delta') for event in events
                                if event.timestamp >= begin_ts and event.timestamp < end_ts
                            ]))

                    item = Node.void('item')
                    summary.add_child(item)
                    item.add_child(Node.s32('year', year))
                    item.add_child(Node.s32('month', month))
                    item.add_child(Node.s32_array('items', items))

            root.add_child(Node.u8('processing', 0))
            root.add_child(lognode)
            return root

        elif method == 'opchpass':
            root = Node.void('eacoin')
            oldpass = request.child_value('passwd')
            newpass = request.child_value('newpasswd')

            if oldpass is None or newpass is None:
                # Refuse to return anything
                print("Invalid eacoin pass change request, missing passwd")
                root.set_attribute('status', str(Status.NO_PROFILE))
                return root

            if self.config['machine']['arcade'] is None:
                # Machine doesn't belong to an arcade
                print("Machine doesn't belong to an arcade")
                root.set_attribute('status', str(Status.NO_PROFILE))
                return root

            arcade = self.data.local.machine.get_arcade(self.config['machine']['arcade'])
            if arcade is None:
                # Refuse to do anything
                print("No arcade for operator pass change request")
                root.set_attribute('status', str(Status.NO_PROFILE))
                return root

            if arcade.pin != oldpass:
                # Refuse to do anything
                print("User entered invalid pin for operator pass change request")
                root.set_attribute('status', str(Status.INVALID_PIN))
                return root

            arcade.pin = newpass
            self.data.local.machine.put_arcade(arcade)
            return root

        elif method == 'checkout':
            session = request.child_value('sessid')
            if session is not None:
                # Destroy the session so it can't be used for any other purchases
                self.data.local.user.destroy_session(session)

            root = Node.void('eacoin')
            return root

        elif method == 'opcheckout':
            session = request.child_value('sessid')
            if session is not None:
                # Destroy the session so it can't be used for any other purchases
                self.data.local.machine.destroy_session(session)

            root = Node.void('eacoin')
            return root

        # Invalid method
        return None
Exemple #30
0
    def unformat_profile(self, userid: UserID, request: Node,
                         oldprofile: ValidatedDict) -> ValidatedDict:
        newprofile = copy.deepcopy(oldprofile)

        # Extract the playmode, important for scores later
        playmode = int(request.attribute('play_mode'))
        newprofile.replace_int('play_mode', playmode)

        # Extract profile options
        newprofile.replace_int('chara', int(request.attribute('chara_num')))
        if 'option' in request.attributes:
            newprofile.replace_int('option', int(request.attribute('option')))
        if 'last_play_flag' in request.attributes:
            newprofile.replace_int('last_play_flag',
                                   int(request.attribute('last_play_flag')))
        if 'medal_and_friend' in request.attributes:
            newprofile.replace_int('medal_and_friend',
                                   int(request.attribute('medal_and_friend')))
        if 'music_num' in request.attributes:
            newprofile.replace_int('music',
                                   int(request.attribute('music_num')))
        if 'sheet_num' in request.attributes:
            newprofile.replace_int('sheet',
                                   int(request.attribute('sheet_num')))
        if 'category_num' in request.attributes:
            newprofile.replace_int('category',
                                   int(request.attribute('category_num')))

        # Keep track of play statistics
        self.update_play_statistics(userid)

        # Extract scores
        for node in request.children:
            if node.name == 'music':
                songid = int(node.attribute('music_num'))
                chart = int(node.attribute('sheet_num'))
                points = int(node.attribute('score'))
                data = int(node.attribute('data'))

                # We never save battle scores
                if chart in [
                        self.GAME_CHART_TYPE_BATTLE_NORMAL,
                        self.GAME_CHART_TYPE_BATTLE_HYPER,
                ]:
                    continue

                # Arrange order to be compatible with future mixes
                if playmode == self.GAME_PLAY_MODE_CHO_CHALLENGE:
                    if chart in [
                            self.GAME_CHART_TYPE_5_BUTTON,
                            self.GAME_CHART_TYPE_ENJOY_5_BUTTON,
                            self.GAME_CHART_TYPE_ENJOY_9_BUTTON,
                    ]:
                        # We don't save 5 button for cho scores, or enjoy modes
                        continue
                    chart = {
                        self.GAME_CHART_TYPE_NORMAL: self.CHART_TYPE_NORMAL,
                        self.GAME_CHART_TYPE_HYPER: self.CHART_TYPE_HYPER,
                        self.GAME_CHART_TYPE_EX: self.CHART_TYPE_EX,
                    }[chart]
                else:
                    chart = {
                        self.GAME_CHART_TYPE_NORMAL:
                        self.CHART_TYPE_OLD_NORMAL,
                        self.GAME_CHART_TYPE_HYPER:
                        self.CHART_TYPE_OLD_HYPER,
                        self.GAME_CHART_TYPE_5_BUTTON:
                        self.CHART_TYPE_5_BUTTON,
                        self.GAME_CHART_TYPE_EX:
                        self.CHART_TYPE_OLD_EX,
                        self.GAME_CHART_TYPE_ENJOY_5_BUTTON:
                        self.CHART_TYPE_ENJOY_5_BUTTON,
                        self.GAME_CHART_TYPE_ENJOY_9_BUTTON:
                        self.CHART_TYPE_ENJOY_9_BUTTON,
                    }[chart]

                # Extract play flags
                shift = {
                    self.CHART_TYPE_5_BUTTON: 4,
                    self.CHART_TYPE_OLD_NORMAL: 0,
                    self.CHART_TYPE_OLD_HYPER: 2,
                    self.CHART_TYPE_OLD_EX: 6,
                    self.CHART_TYPE_NORMAL: 0,
                    self.CHART_TYPE_HYPER: 2,
                    self.CHART_TYPE_EX: 6,
                    self.CHART_TYPE_ENJOY_5_BUTTON: 9,
                    self.CHART_TYPE_ENJOY_9_BUTTON: 8,
                }[chart]

                if chart in [
                        self.CHART_TYPE_ENJOY_5_BUTTON,
                        self.CHART_TYPE_ENJOY_9_BUTTON,
                ]:
                    # We only store cleared or not played for enjoy mode
                    mask = 0x1
                else:
                    # We store all data for regular charts
                    mask = 0x3

                # Grab flags, map to medals in DB. Choose lowest one for each so
                # a newer pop'n can still improve scores and medals.
                flags = (data >> shift) & mask
                medal = {
                    self.GAME_PLAY_FLAG_FAILED: self.PLAY_MEDAL_CIRCLE_FAILED,
                    self.GAME_PLAY_FLAG_CLEARED:
                    self.PLAY_MEDAL_CIRCLE_CLEARED,
                    self.GAME_PLAY_FLAG_FULL_COMBO:
                    self.PLAY_MEDAL_CIRCLE_FULL_COMBO,
                    self.GAME_PLAY_FLAG_PERFECT_COMBO: self.PLAY_MEDAL_PERFECT,
                }[flags]
                self.update_score(userid, songid, chart, points, medal)

        return newprofile