Esempio n. 1
0
    def handle_gameend_regist_request(self, request: Node) -> Node:
        data = request.child('data')
        player = data.child('player')

        if player is not None:
            refid = player.child_value('refid')
        else:
            refid = None

        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)

        gameend = Node.void('gameend')
        data = Node.void('data')
        gameend.add_child(data)
        player = Node.void('player')
        data.add_child(player)
        player.add_child(Node.s32('session_id', 1))
        player.add_child(Node.s32('end_final_session_id', 1))
        return gameend
Esempio n. 2
0
    def unformat_profile(self, userid: UserID, request: Node,
                         oldprofile: ValidatedDict,
                         is_new: bool) -> ValidatedDict:
        # Profile save request, data values are base64 encoded.
        # d is a CSV, and bin1 is binary data.
        newprofile = copy.deepcopy(oldprofile)
        strdatas: List[bytes] = []
        bindatas: List[bytes] = []

        record = request.child('data/record')
        for node in record.children:
            if node.name != 'd':
                continue

            profile = base64.b64decode(node.value)
            # Update the shop name if this is a new profile, since we know it came
            # from this cabinet. This is the only source of truth for what the
            # cabinet shop name is set to.
            if is_new:
                self.__update_shop_name(profile)
            strdatas.append(profile)
            bindatas.append(base64.b64decode(node.child_value('bin1')))

        newprofile['strdatas'] = strdatas
        newprofile['bindatas'] = bindatas

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

        return newprofile
Esempio n. 3
0
 def handle_gametop_regist_request(self, request: Node) -> Node:
     data = request.child('data')
     player = data.child('player')
     passnode = player.child('pass')
     refid = passnode.child_value('refid')
     name = player.child_value('name')
     root = self.new_profile_by_refid(refid, name)
     return root
Esempio n. 4
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
Esempio n. 5
0
 def handle_gametop_get_mdata_request(self, request: Node) -> Node:
     data = request.child('data')
     player = data.child('player')
     extid = player.child_value('jid')
     root = self.get_scores_by_extid(extid)
     if root is None:
         root = Node.void('gametop')
         root.set_attribute('status', str(Status.NO_PROFILE))
     return root
Esempio n. 6
0
 def handle_gametop_get_pdata_request(self, request: Node) -> Node:
     data = request.child('data')
     player = data.child('player')
     passnode = player.child('pass')
     refid = passnode.child_value('refid')
     root = self.get_profile_by_refid(refid)
     if root is None:
         root = Node.void('gametop')
         root.set_attribute('status', str(Status.NO_PROFILE))
     return root
Esempio n. 7
0
    def handle_playerdata_usergamedata_advanced_request(
            self, request: Node) -> Optional[Node]:
        playerdata = Node.void('playerdata')

        # DDR Ace decides to be difficult and have a third level of packet switching
        mode = request.child_value('data/mode')
        refid = request.child_value('data/refid')
        extid = request.child_value('data/ddrcode')

        userid = self.data.remote.user.from_refid(self.game, self.version,
                                                  refid)
        if userid is None:
            # Possibly look up by extid instead
            userid = self.data.remote.user.from_extid(self.game, self.version,
                                                      extid)

        if mode == 'userload':
            self.__handle_userload(userid, request.child('data'), playerdata)
        elif mode == 'usersave':
            self.__handle_usersave(userid, request.child('data'), playerdata)
        elif mode == 'rivalload':
            self.__handle_rivalload(userid, request.child('data'), playerdata)
        elif mode == 'usernew':
            self.__handle_usernew(userid, request.child('data'), playerdata)
        elif mode == 'inheritance':
            self.__handle_inheritance(userid, request.child('data'),
                                      playerdata)
        elif mode == 'ghostload':
            self.__handle_ghostload(userid, request.child('data'), playerdata)
        else:
            # We don't support this
            return None

        playerdata.add_child(Node.s32('result', 0))
        return playerdata
Esempio n. 8
0
    def unformat_profile(self, userid: UserID, request: Node, oldprofile: ValidatedDict) -> ValidatedDict:
        newprofile = copy.deepcopy(oldprofile)

        # Update experience and in-game currencies
        earned_gamecoin_packet = request.child_value('earned_gamecoin_packet')
        if earned_gamecoin_packet is not None:
            newprofile.replace_int('packet', newprofile.get_int('packet') + earned_gamecoin_packet)
        earned_gamecoin_block = request.child_value('earned_gamecoin_block')
        if earned_gamecoin_block is not None:
            newprofile.replace_int('block', newprofile.get_int('block') + earned_gamecoin_block)
        gain_exp = request.child_value('gain_exp')
        if gain_exp is not None:
            newprofile.replace_int('exp', newprofile.get_int('exp') + gain_exp)

        # Miscelaneous stuff
        newprofile.replace_int('m_user_cnt', request.child_value('m_user_cnt'))

        # Update user's unlock status if we aren't force unlocked
        game_config = self.get_game_config()
        if not game_config.get_bool('force_unlock_cards'):
            have_item = request.child_value('have_item')
            if have_item is not None:
                newprofile.replace_int_array('have_item', 512, [1 if x else 0 for x in have_item])
        if not game_config.get_bool('force_unlock_songs'):
            have_note = request.child_value('have_note')
            if have_note is not None:
                newprofile.replace_int_array('have_note', 512, [1 if x else 0 for x in have_note])

        # Grab last information.
        lastdict = newprofile.get_dict('last')
        lastdict.replace_int('headphone', request.child_value('headphone'))
        lastdict.replace_int('hispeed', request.child_value('hispeed'))
        lastdict.replace_int('appeal_id', request.child_value('appeal_id'))
        lastdict.replace_int('frame0', request.child_value('frame0'))
        lastdict.replace_int('frame1', request.child_value('frame1'))
        lastdict.replace_int('frame2', request.child_value('frame2'))
        lastdict.replace_int('frame3', request.child_value('frame3'))
        lastdict.replace_int('frame4', request.child_value('frame4'))
        last = request.child('last')
        if last is not None:
            lastdict.replace_int('music_id', intish(last.attribute('music_id')))
            lastdict.replace_int('music_type', intish(last.attribute('music_type')))
            lastdict.replace_int('sort_type', intish(last.attribute('sort_type')))

        # Save back last information gleaned from results
        newprofile.replace_dict('last', lastdict)

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

        return newprofile
Esempio n. 9
0
    def handle_playerdata_usergamedata_send_request(self, request: Node) -> Node:
        playerdata = Node.void('playerdata')
        refid = request.child_value('data/refid')

        userid = self.data.remote.user.from_refid(self.game, self.version, refid)
        if userid is not None:
            profile = self.get_profile(userid) or ValidatedDict()
            usergamedata = profile.get_dict('usergamedata')

            for record in request.child('data/record').children:
                if record.name != 'd':
                    continue

                strdata = base64.b64decode(record.value)
                bindata = base64.b64decode(record.child_value('bin1'))

                # Grab and format the profile objects
                strdatalist = strdata.split(b',')
                profiletype = strdatalist[1].decode('utf-8')
                strdatalist = strdatalist[2:]

                # Extract relevant bits for frontend/API
                if profiletype == 'COMMON':
                    profile.replace_str('name', strdatalist[self.GAME_COMMON_NAME_OFFSET].decode('ascii'))
                    profile.replace_int('area', intish(strdatalist[self.GAME_COMMON_AREA_OFFSET].decode('ascii'), 16))
                    profile.replace_bool('workout_mode', int(strdatalist[self.GAME_COMMON_WEIGHT_DISPLAY_OFFSET].decode('ascii'), 16) != 0)
                    profile.replace_int('weight', int(float(strdatalist[self.GAME_COMMON_WEIGHT_OFFSET].decode('ascii')) * 10))
                    profile.replace_int('character', int(strdatalist[self.GAME_COMMON_CHARACTER_OFFSET].decode('ascii'), 16))
                if profiletype == 'OPTION':
                    profile.replace_int('combo', int(strdatalist[self.GAME_OPTION_COMBO_POSITION_OFFSET].decode('ascii'), 16))
                    profile.replace_int('early_late', int(strdatalist[self.GAME_OPTION_FAST_SLOW_OFFSET].decode('ascii'), 16))
                    profile.replace_int('arrowskin', int(strdatalist[self.GAME_OPTION_ARROW_SKIN_OFFSET].decode('ascii'), 16))
                    profile.replace_int('guidelines', int(strdatalist[self.GAME_OPTION_GUIDELINE_OFFSET].decode('ascii'), 16))
                    profile.replace_int('filter', int(strdatalist[self.GAME_OPTION_FILTER_OFFSET].decode('ascii'), 16))

                usergamedata[profiletype] = {
                    'strdata': b','.join(strdatalist),
                    'bindata': bindata,
                }

            profile.replace_dict('usergamedata', usergamedata)
            self.put_profile(userid, profile)

        playerdata.add_child(Node.s32('result', 0))
        return playerdata
Esempio n. 10
0
 def handle_gametop_get_mdata_request(self, request: Node) -> Node:
     data = request.child('data')
     player = data.child('player')
     extid = player.child_value('jid')
     mdata_ver = player.child_value('mdata_ver')  # Game requests mdata 3 times per profile for some reason
     if mdata_ver != 1:
         root = Node.void('gametop')
         datanode = Node.void('data')
         root.add_child(datanode)
         player = Node.void('player')
         datanode.add_child(player)
         player.add_child(Node.s32('jid', extid))
         playdata = Node.void('mdata_list')
         player.add_child(playdata)
         return root
     root = self.get_scores_by_extid(extid)
     if root is None:
         root = Node.void('gametop')
         root.set_attribute('status', str(Status.NO_PROFILE))
     return root
Esempio n. 11
0
    def handle_lobby_entry_request(self, request: Node) -> Node:
        root = Node.void('lobby')
        data = Node.void('data')
        root.add_child(data)
        roomid = Node.s64('roomid', -2)
        roomid.set_attribute('master', '1')
        data.add_child(roomid)
        refresh_intr = Node.s16('refresh_intr', 3)
        data.add_child(refresh_intr)

        # Grab music id from the request
        request_data = request.child('data')
        request_music = request_data.child('music')
        music_id = request_music.child('id')
        seq_id = request_music.child('seq')
        music = Node.void('music')
        music.add_child(music_id)
        music.add_child(seq_id)
        data.add_child(music)
        return root
Esempio n. 12
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
Esempio n. 13
0
    def unformat_profile(self, userid: UserID, request: Node,
                         oldprofile: ValidatedDict) -> ValidatedDict:
        newprofile = copy.deepcopy(oldprofile)
        newprofile.replace_int('option', request.child_value('option'))
        newprofile.replace_int('chara', request.child_value('chara'))
        newprofile.replace_int('mode', request.child_value('mode'))
        newprofile.replace_int('button', request.child_value('button'))
        newprofile.replace_int('music', request.child_value('music'))
        newprofile.replace_int('sheet', request.child_value('sheet'))
        newprofile.replace_int('last_play_flag',
                               request.child_value('last_play_flag'))
        newprofile.replace_int('category', request.child_value('category'))
        newprofile.replace_int('sub_category',
                               request.child_value('sub_category'))
        newprofile.replace_int('chara_category',
                               request.child_value('chara_category'))
        newprofile.replace_int('medal_and_friend',
                               request.child_value('medal_and_friend'))
        newprofile.replace_int('ep', request.child_value('ep'))
        newprofile.replace_int_array('sp_color_flg', 2,
                                     request.child_value('sp_color_flg'))
        newprofile.replace_int('read_news', request.child_value('read_news'))
        newprofile.replace_int('consecutive_days_coupon',
                               request.child_value('consecutive_days_coupon'))
        newprofile.replace_int('tutorial', request.child_value('tutorial'))
        newprofile.replace_int('music_open_pt',
                               request.child_value('music_open_pt'))
        newprofile.replace_int_array('gitadora_point', 3,
                                     request.child_value('gitadora_point'))
        newprofile.replace_int('gitadora_select',
                               request.child_value('gitadora_select'))

        sp_node = request.child('sp_data')
        if sp_node is not None:
            newprofile.replace_int('sp', sp_node.child_value('sp'))

        zoo_dict = newprofile.get_dict('zoo')
        zoo_node = request.child('zoo')
        if zoo_node is not None:
            zoo_dict.replace_int_array('point', 5,
                                       zoo_node.child_value('point'))
            zoo_dict.replace_int_array('music_list', 2,
                                       zoo_node.child_value('music_list'))
            zoo_dict.replace_int_array('today_play_flag', 4,
                                       zoo_node.child_value('today_play_flag'))
        newprofile.replace_dict('zoo', zoo_dict)

        avatar_dict = newprofile.get_dict('avatar')
        avatar_dict.replace_int('hair', request.child_value('hair'))
        avatar_dict.replace_int('face', request.child_value('face'))
        avatar_dict.replace_int('body', request.child_value('body'))
        avatar_dict.replace_int('effect', request.child_value('effect'))
        avatar_dict.replace_int('object', request.child_value('object'))
        avatar_dict.replace_int_array('comment', 2,
                                      request.child_value('comment'))
        avatar_dict.replace_int_array('get_hair', 2,
                                      request.child_value('get_hair'))
        avatar_dict.replace_int_array('get_face', 2,
                                      request.child_value('get_face'))
        avatar_dict.replace_int_array('get_body', 2,
                                      request.child_value('get_body'))
        avatar_dict.replace_int_array('get_effect', 2,
                                      request.child_value('get_effect'))
        avatar_dict.replace_int_array('get_object', 2,
                                      request.child_value('get_object'))
        avatar_dict.replace_int_array('get_comment_over', 3,
                                      request.child_value('get_comment_over'))
        avatar_dict.replace_int_array('get_comment_under', 3,
                                      request.child_value('get_comment_under'))
        newprofile.replace_dict('avatar', avatar_dict)

        avatar_add_dict = newprofile.get_dict('avatar_add')
        avatar_add_node = request.child('avatar_add')
        if avatar_add_node is not None:
            avatar_add_dict.replace_int_array(
                'get_hair', 2, avatar_add_node.child_value('get_hair'))
            avatar_add_dict.replace_int_array(
                'get_face', 2, avatar_add_node.child_value('get_face'))
            avatar_add_dict.replace_int_array(
                'get_body', 2, avatar_add_node.child_value('get_body'))
            avatar_add_dict.replace_int_array(
                'get_effect', 2, avatar_add_node.child_value('get_effect'))
            avatar_add_dict.replace_int_array(
                'get_object', 2, avatar_add_node.child_value('get_object'))
            avatar_add_dict.replace_int_array(
                'get_comment_over', 2,
                avatar_add_node.child_value('get_comment_over'))
            avatar_add_dict.replace_int_array(
                'get_comment_under', 2,
                avatar_add_node.child_value('get_comment_under'))
            avatar_add_dict.replace_int_array(
                'new_hair', 2, avatar_add_node.child_value('new_hair'))
            avatar_add_dict.replace_int_array(
                'new_face', 2, avatar_add_node.child_value('new_face'))
            avatar_add_dict.replace_int_array(
                'new_body', 2, avatar_add_node.child_value('new_body'))
            avatar_add_dict.replace_int_array(
                'new_effect', 2, avatar_add_node.child_value('new_effect'))
            avatar_add_dict.replace_int_array(
                'new_object', 2, avatar_add_node.child_value('new_object'))
            avatar_add_dict.replace_int_array(
                'new_comment_over', 2,
                avatar_add_node.child_value('new_comment_over'))
            avatar_add_dict.replace_int_array(
                'new_comment_under', 2,
                avatar_add_node.child_value('new_comment_under'))
        newprofile.replace_dict('avatar_add', avatar_add_dict)

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

        # Extract scores
        for node in request.children:
            if node.name == 'stage':
                songid = node.child_value('no')
                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,
                }[node.child_value('sheet')]
                medal = (node.child_value('n_data') >> (chart * 4)) & 0x000F
                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_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]
                points = node.child_value('score')
                self.update_score(userid, songid, chart, points, medal)

        return newprofile
Esempio n. 14
0
    def unformat_profile(self, userid: UserID, request: Node, oldprofile: ValidatedDict) -> ValidatedDict:
        newprofile = copy.deepcopy(oldprofile)
        data = request.child('data')

        # Grab player information
        player = data.child('player')

        # Grab last information. Lots of this will be filled in while grabbing scores
        last = newprofile.get_dict('last')
        last.replace_int('play_time', player.child_value('time_gameend'))
        last.replace_str('shopname', player.child_value('shopname'))
        last.replace_str('areaname', player.child_value('areaname'))

        # Grab player info for echoing back
        info = player.child('info')
        if info is not None:
            newprofile.replace_int('jubility', info.child_value('jubility'))
            newprofile.replace_int('jubility_yday', info.child_value('jubility_yday'))
            newprofile.replace_int('tune_cnt', info.child_value('tune_cnt'))
            newprofile.replace_int('save_cnt', info.child_value('save_cnt'))
            newprofile.replace_int('saved_cnt', info.child_value('saved_cnt'))
            newprofile.replace_int('fc_cnt', info.child_value('fc_cnt'))
            newprofile.replace_int('ex_cnt', info.child_value('exc_cnt'))  # Not a mistake, Jubeat is weird
            newprofile.replace_int('pf_cnt', info.child_value('pf_cnt'))
            newprofile.replace_int('clear_cnt', info.child_value('clear_cnt'))
            newprofile.replace_int('match_cnt', info.child_value('match_cnt'))
            newprofile.replace_int('beat_cnt', info.child_value('beat_cnt'))
            newprofile.replace_int('total_best_score', info.child_value('total_best_score'))
            newprofile.replace_int('mynews_cnt', info.child_value('mynews_cnt'))
            newprofile.replace_int('extra_point', info.child_value('extra_point'))
            newprofile.replace_bool('is_extra_played', info.child_value('is_extra_played'))

            last.replace_int('expert_option', info.child_value('expert_option'))
            last.replace_int('matching', info.child_value('matching'))
            last.replace_int('hazard', info.child_value('hazard'))
            last.replace_int('hard', info.child_value('hard'))

        # Grab unlock progress
        item = player.child('item')
        if item is not None:
            newprofile.replace_int_array('secret_list', 32, item.child_value('secret_list'))
            newprofile.replace_int_array('title_list', 96, item.child_value('title_list'))
            newprofile.replace_int('theme_list', item.child_value('theme_list'))
            newprofile.replace_int_array('marker_list', 2, item.child_value('marker_list'))
            newprofile.replace_int_array('parts_list', 96, item.child_value('parts_list'))
            newprofile.replace_int_array('secret_list_new', 32, item.child_value('secret_new'))
            newprofile.replace_int_array('title_list_new', 96, item.child_value('title_new'))
            newprofile.replace_int('theme_list_new', item.child_value('theme_new'))
            newprofile.replace_int_array('marker_list_new', 2, item.child_value('marker_new'))

        # Grab macchiato event
        macchiatodict = newprofile.get_dict('macchiato')
        macchiato = player.child('macchiato')
        if macchiato is not None:
            macchiatodict.replace_int('pack_id', macchiato.child_value('pack_id'))
            macchiatodict.replace_int('bean_num', macchiato.child_value('bean_num'))
            macchiatodict.replace_int('daily_milk_num', macchiato.child_value('daily_milk_num'))
            macchiatodict.replace_bool('is_received_daily_milk', macchiato.child_value('is_received_daily_milk'))
            macchiatodict.replace_bool('sub_menu_is_completed', macchiato.child_value('sub_menu_is_completed'))
            macchiatodict.replace_int('today_tune_cnt', macchiato.child_value('today_tune_cnt'))
            macchiatodict.replace_int_array('daily_milk_bonus', 9, macchiato.child_value('daily_milk_bonus'))
            macchiatodict.replace_int('compensation_milk', macchiato.child_value('compensation_milk'))
            macchiatodict.replace_int('match_cnt', macchiato.child_value('match_cnt'))
            macchiatodict.replace_int('used_bean', macchiato.child_value('used_bean'))
            macchiatodict.replace_int('used_milk', macchiato.child_value('used_milk'))
            macchiatodict.replace_int('daily_play_burst', macchiato.child_value('daily_play_burst'))
        newprofile.replace_dict('macchiato', macchiatodict)

        # Get timestamps for played songs
        timestamps: Dict[int, int] = {}
        history = player.child('history')
        if history is not None:
            for tune in history.children:
                if tune.name != 'tune':
                    continue
                entry = int(tune.attribute('log_id'))
                ts = int(tune.child_value('timestamp') / 1000)
                timestamps[entry] = ts

        # Grab scores and save those
        result = data.child('result')
        if result is not None:
            for tune in result.children:
                if tune.name != 'tune':
                    continue
                result = tune.child('player')

                last.replace_int('marker', tune.child_value('marker'))
                last.replace_int('title', tune.child_value('title'))
                last.replace_int('parts', tune.child_value('parts'))
                last.replace_int('theme', tune.child_value('theme'))
                last.replace_int('sort', tune.child_value('sort'))
                last.replace_int('category', tune.child_value('category'))
                last.replace_int('rank_sort', tune.child_value('rank_sort'))
                last.replace_int('combo_disp', tune.child_value('combo_disp'))

                songid = tune.child_value('music')
                entry = int(tune.attribute('id'))
                timestamp = timestamps.get(entry, Time.now())
                chart = int(result.child('score').attribute('seq'))
                points = result.child_value('score')
                flags = int(result.child('score').attribute('clear'))
                combo = int(result.child('score').attribute('combo'))
                ghost = result.child_value('mbar')

                # Miscelaneous last data for echoing to profile get
                last.replace_int('music_id', songid)
                last.replace_int('seq_id', chart)

                mapping = {
                    self.GAME_FLAG_BIT_CLEARED: self.PLAY_MEDAL_CLEARED,
                    self.GAME_FLAG_BIT_FULL_COMBO: self.PLAY_MEDAL_FULL_COMBO,
                    self.GAME_FLAG_BIT_EXCELLENT: self.PLAY_MEDAL_EXCELLENT,
                    self.GAME_FLAG_BIT_NEARLY_FULL_COMBO: self.PLAY_MEDAL_NEARLY_FULL_COMBO,
                    self.GAME_FLAG_BIT_NEARLY_EXCELLENT: self.PLAY_MEDAL_NEARLY_EXCELLENT,
                }

                # Figure out the highest medal based on bits passed in
                medal = self.PLAY_MEDAL_FAILED
                for bit in mapping:
                    if flags & bit > 0:
                        medal = max(medal, mapping[bit])

                self.update_score(userid, timestamp, songid, chart, points, medal, combo, ghost)

        # Grab the course results as well
        course = data.child('course')
        if course is not None:
            courseid = course.child_value('course_id')
            rating = {
                self.GAME_COURSE_RATING_FAILED: self.COURSE_RATING_FAILED,
                self.GAME_COURSE_RATING_BRONZE: self.COURSE_RATING_BRONZE,
                self.GAME_COURSE_RATING_SILVER: self.COURSE_RATING_SILVER,
                self.GAME_COURSE_RATING_GOLD: self.COURSE_RATING_GOLD,
            }[course.child_value('rating')]
            scores = [0] * 5
            for music in course.children:
                if music.name != 'music':
                    continue
                index = int(music.attribute('index'))
                scores[index] = music.child_value('score')

            # Save course itself
            self.save_course(userid, courseid, rating, scores)

            # Save the last course ID
            last.replace_int('last_course_id', courseid)

        # Save back last information gleaned from results
        newprofile.replace_dict('last', last)

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

        return newprofile
Esempio n. 15
0
    def unformat_profile(self, userid: UserID, request: Node,
                         oldprofile: ValidatedDict) -> ValidatedDict:
        game_config = self.get_game_config()
        newprofile = copy.deepcopy(oldprofile)

        newprofile.replace_int('lid',
                               ID.parse_machine_id(request.child_value('lid')))
        newprofile.replace_str('name', request.child_value('pdata/base/name'))
        newprofile.replace_int('lvl', request.child_value('pdata/base/lv'))
        newprofile.replace_int('exp', request.child_value('pdata/base/exp'))
        newprofile.replace_int('mg', request.child_value('pdata/base/mg'))
        newprofile.replace_int('ap', request.child_value('pdata/base/ap'))
        newprofile.replace_int('flag', request.child_value('pdata/base/flag'))

        customdict = newprofile.get_dict('custom')
        custom = request.child('pdata/custom')
        if custom:
            customdict.replace_int('bgm_m', custom.child_value('bgm_m'))
            customdict.replace_int('st_f', custom.child_value('st_f'))
            customdict.replace_int('st_bg', custom.child_value('st_bg'))
            customdict.replace_int('st_bg_b', custom.child_value('st_bg_b'))
            customdict.replace_int('eff_e', custom.child_value('eff_e'))
            customdict.replace_int('se_s', custom.child_value('se_s'))
            customdict.replace_int('se_s_v', custom.child_value('se_s_v'))
        newprofile.replace_dict('custom', customdict)

        # Music unlocks and other stuff
        released = request.child('pdata/released')
        if released:
            for child in released.children:
                if child.name != 'info':
                    continue

                item_id = child.child_value('id')
                item_type = child.child_value('type')
                if game_config.get_bool(
                        'force_unlock_songs') and item_type == 0:
                    # Don't save unlocks when we're force unlocking
                    continue

                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    item_id,
                    'item_{}'.format(item_type),
                    {},
                )

        # Grab any new records set during this play session. Reflec Beat original only sends
        # the top record back for songs that were played at least once during the session.
        # Note that it sends the top record, so if you play the song twice, it will return
        # only one record. Also, if you get a lower score than a previous try, it will return
        # the previous try. So, we must also look at the battle log for the actual play scores,
        # and combine the data if we can.
        savedrecords: Dict[int, Dict[int, Dict[str, int]]] = {}
        songplays = request.child('pdata/record')
        if songplays:
            for child in songplays.children:
                if child.name != 'rec':
                    continue

                songid = child.child_value('mid')
                chart = child.child_value('ng')

                # These don't get sent with the battle logs, so we try to construct
                # the values here.
                if songid not in savedrecords:
                    savedrecords[songid] = {}
                savedrecords[songid][chart] = {
                    'achievement_rate': child.child_value('ar') * 10,
                    'points': child.child_value('bs'),
                    'combo': child.child_value('mc'),
                    'miss_count': child.child_value('bmc'),
                    'win': child.child_value('win'),
                    'lose': child.child_value('lose'),
                    'draw': child.child_value('draw'),
                }

        # Now, see the actual battles that were played. If we can, unify the data with a record.
        # We only do that when the record achievement rate and score matches the battle achievement
        # rate and score, so we know for a fact that that record was generated by this battle.
        battlelogs = request.child('pdata/blog')
        if battlelogs:
            for child in battlelogs.children:
                if child.name != 'log':
                    continue

                songid = child.child_value('mid')
                chart = child.child_value('ng')

                clear_type = child.child_value('myself/ct')
                achievement_rate = child.child_value('myself/ar') * 10
                points = child.child_value('myself/s')

                clear_type, combo_type = self.__game_to_db_clear_type(
                    clear_type, achievement_rate)

                combo = None
                miss_count = -1
                stats = None

                if songid in savedrecords:
                    if chart in savedrecords[songid]:
                        data = savedrecords[songid][chart]

                        if (data['achievement_rate'] == achievement_rate
                                and data['points'] == points):
                            # This is the same record! Use the stats from it to update our
                            # internal representation.
                            combo = data['combo']
                            miss_count = data['miss_count']
                            stats = {
                                'win': data['win'],
                                'lose': data['lose'],
                                'draw': data['draw'],
                            }

                self.update_score(
                    userid,
                    songid,
                    chart,
                    points,
                    achievement_rate,
                    clear_type,
                    combo_type,
                    miss_count,
                    combo=combo,
                    stats=stats,
                )

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

        return newprofile
Esempio n. 16
0
    def unformat_profile(self, userid: UserID, request: Node, oldprofile: ValidatedDict) -> ValidatedDict:
        newprofile = copy.deepcopy(oldprofile)

        # Update blaster energy and in-game currencies
        earned_gamecoin_packet = request.child_value('earned_gamecoin_packet')
        if earned_gamecoin_packet is not None:
            newprofile.replace_int('packet', newprofile.get_int('packet') + earned_gamecoin_packet)
        earned_gamecoin_block = request.child_value('earned_gamecoin_block')
        if earned_gamecoin_block is not None:
            newprofile.replace_int('block', newprofile.get_int('block') + earned_gamecoin_block)
        earned_blaster_energy = request.child_value('earned_blaster_energy')
        if earned_blaster_energy is not None:
            newprofile.replace_int('blaster_energy', newprofile.get_int('blaster_energy') + earned_blaster_energy)

        # Miscelaneous stuff
        newprofile.replace_int('blaster_count', request.child_value('blaster_count'))
        newprofile.replace_int('skill_name_id', request.child_value('skill_name_id'))
        newprofile.replace_int_array('hidden_param', 20, request.child_value('hidden_param'))

        # Update user's unlock status if we aren't force unlocked
        game_config = self.get_game_config()

        if request.child('item') is not None:
            for child in request.child('item').children:
                if child.name != 'info':
                    continue

                item_id = child.child_value('id')
                item_type = child.child_value('type')
                param = child.child_value('param')
                diff_param = child.child_value('diff_param')

                if game_config.get_bool('force_unlock_songs') and item_type == self.GAME_CATALOG_TYPE_SONG:
                    # Don't save back songs, because they were force unlocked
                    continue

                if diff_param is not None:
                    paramvals = {
                        'diff_param': diff_param,
                        'param': param,
                    }
                else:
                    paramvals = {
                        'param': param,
                    }

                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    item_id,
                    f'item_{item_type}',
                    paramvals,
                )

        # Grab last information.
        lastdict = newprofile.get_dict('last')
        lastdict.replace_int('headphone', request.child_value('headphone'))
        lastdict.replace_int('appeal_id', request.child_value('appeal_id'))
        lastdict.replace_int('comment_id', request.child_value('comment_id'))
        lastdict.replace_int('music_id', request.child_value('music_id'))
        lastdict.replace_int('music_type', request.child_value('music_type'))
        lastdict.replace_int('sort_type', request.child_value('sort_type'))
        lastdict.replace_int('narrow_down', request.child_value('narrow_down'))
        lastdict.replace_int('gauge_option', request.child_value('gauge_option'))

        # Save back last information gleaned from results
        newprofile.replace_dict('last', lastdict)

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

        return newprofile
Esempio n. 17
0
    def unformat_profile(self, userid: UserID, request: Node,
                         oldprofile: ValidatedDict) -> ValidatedDict:
        # For some reason, Pop'n 20 sends us two profile saves, one with 'not done yet'
        # so we only want to process the done yet node. The 'not gameover' save has
        # jubeat collabo stuff set in it, but we don't use that so it doesn't matter.
        if request.child_value('is_not_gameover') == 1:
            return oldprofile

        newprofile = copy.deepcopy(oldprofile)
        newprofile.replace_int('option', request.child_value('option'))
        newprofile.replace_int('chara', request.child_value('chara'))
        newprofile.replace_int('mode', request.child_value('mode'))
        newprofile.replace_int('button', request.child_value('button'))
        newprofile.replace_int('music', request.child_value('music'))
        newprofile.replace_int('sheet', request.child_value('sheet'))
        newprofile.replace_int('last_play_flag',
                               request.child_value('last_play_flag'))
        newprofile.replace_int('category', request.child_value('category'))
        newprofile.replace_int('sub_category',
                               request.child_value('sub_category'))
        newprofile.replace_int('chara_category',
                               request.child_value('chara_category'))
        newprofile.replace_int('medal_and_friend',
                               request.child_value('medal_and_friend'))
        newprofile.replace_int('ep', request.child_value('ep'))
        newprofile.replace_int_array('sp_color_flg', 2,
                                     request.child_value('sp_color_flg'))
        newprofile.replace_int('read_news', request.child_value('read_news'))
        newprofile.replace_int('consecutive_days_coupon',
                               request.child_value('consecutive_days_coupon'))
        newprofile.replace_int('tutorial', request.child_value('tutorial'))
        newprofile.replace_int('music_open_pt',
                               request.child_value('music_open_pt'))
        newprofile.replace_int('collabo', request.child_value('collabo'))

        sp_node = request.child('sp_data')
        if sp_node is not None:
            newprofile.replace_int('sp', sp_node.child_value('sp'))

        reflec_node = request.child('reflec_data')
        if reflec_node is not None:
            newprofile.replace_int_array('reflec', 2,
                                         reflec_node.child_value('reflec'))

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

        # Extract player card stuff
        player_card_dict = newprofile.get_dict('player_card')
        player_card_dict.replace_int_array('title', 2,
                                           request.child_value('title'))
        player_card_dict.replace_int('frame', request.child_value('frame'))
        player_card_dict.replace_int('base', request.child_value('base'))
        player_card_dict.replace_int_array('seal', 2,
                                           request.child_value('seal'))
        player_card_dict.replace_int_array('get_title', 4,
                                           request.child_value('get_title'))
        player_card_dict.replace_int('get_frame',
                                     request.child_value('get_frame'))
        player_card_dict.replace_int('get_base',
                                     request.child_value('get_base'))
        player_card_dict.replace_int_array('get_seal', 2,
                                           request.child_value('get_seal'))

        player_card_ex = request.child('player_card_ex')
        if player_card_ex is not None:
            player_card_dict.replace_int(
                'get_title_ex', player_card_ex.child_value('get_title_ex'))
            player_card_dict.replace_int(
                'get_frame_ex', player_card_ex.child_value('get_frame_ex'))
            player_card_dict.replace_int(
                'get_base_ex', player_card_ex.child_value('get_base_ex'))
            player_card_dict.replace_int(
                'get_seal_ex', player_card_ex.child_value('get_seal_ex'))
        newprofile.replace_dict('player_card', player_card_dict)

        # Extract navigate stuff
        navigate_dict = newprofile.get_dict('navigate')
        navigate = request.child('navigate')
        if navigate is not None:
            navigate_dict.replace_int('genre', navigate.child_value('genre'))
            navigate_dict.replace_int('image', navigate.child_value('image'))
            navigate_dict.replace_int('level', navigate.child_value('level'))
            navigate_dict.replace_int('ojama', navigate.child_value('ojama'))
            navigate_dict.replace_int('limit_num',
                                      navigate.child_value('limit_num'))
            navigate_dict.replace_int('button', navigate.child_value('button'))
            navigate_dict.replace_int('life', navigate.child_value('life'))
            navigate_dict.replace_int('progress',
                                      navigate.child_value('progress'))
        newprofile.replace_dict('navigate', navigate_dict)

        # Extract scores
        for node in request.children:
            if node.name == 'stage':
                songid = node.child_value('no')
                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,
                }[node.child_value('sheet')]
                medal = (node.child_value('n_data') >> (chart * 4)) & 0x000F
                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_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]
                points = node.child_value('score')
                self.update_score(userid, songid, chart, points, medal)

        return newprofile
Esempio n. 18
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
Esempio n. 19
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
Esempio n. 20
0
    def handle_gametop_get_course_request(self, request: Node) -> Node:
        data = request.child('data')
        player = data.child('player')
        extid = player.child_value('jid')

        gametop = Node.void('gametop')
        data = Node.void('data')
        gametop.add_child(data)

        # Course list available
        course_list = Node.void('course_list')
        data.add_child(course_list)

        validcourses: List[int] = []
        for course in self.get_all_courses():
            coursenode = Node.void('course')
            course_list.add_child(coursenode)

            # Basic course info
            if course['id'] in validcourses:
                raise Exception('Cannot have same course ID specified twice!')
            validcourses.append(course['id'])
            coursenode.add_child(Node.s32('id', course['id']))
            coursenode.add_child(Node.string('name', course['name']))
            coursenode.add_child(Node.u8('level', course['level']))

            # Translate internal to game
            def translate_req(internal_req: int) -> int:
                return {
                    self.COURSE_REQUIREMENT_SCORE: self.GAME_COURSE_REQUIREMENT_SCORE,
                    self.COURSE_REQUIREMENT_FULL_COMBO: self.GAME_COURSE_REQUIREMENT_FULL_COMBO,
                    self.COURSE_REQUIREMENT_PERFECT_PERCENT: self.GAME_COURSE_REQUIREMENT_PERFECT_PERCENT,
                }.get(internal_req, 0)

            # Course bronze/silver/gold rules
            ids = [0] * 3
            bronze_values = [0] * 3
            silver_values = [0] * 3
            gold_values = [0] * 3
            slot = 0
            for req in course['requirements']:
                req_values = course['requirements'][req]

                ids[slot] = translate_req(req)
                bronze_values[slot] = req_values[0]
                silver_values[slot] = req_values[1]
                gold_values[slot] = req_values[2]
                slot = slot + 1

            norma = Node.void('norma')
            coursenode.add_child(norma)
            norma.add_child(Node.s32_array('norma_id', ids))
            norma.add_child(Node.s32_array('bronze_value', bronze_values))
            norma.add_child(Node.s32_array('silver_value', silver_values))
            norma.add_child(Node.s32_array('gold_value', gold_values))

            # Music list for course
            music_index = 0
            music_list = Node.void('music_list')
            coursenode.add_child(music_list)

            for entry in course['music']:
                music = Node.void('music')
                music.set_attribute('index', str(music_index))
                music_list.add_child(music)
                music.add_child(Node.s32('music_id', entry[0]))
                music.add_child(Node.u8('seq', entry[1]))
                music_index = music_index + 1

        # Look up profile so we can load the last course played
        userid = self.data.remote.user.from_extid(self.game, self.version, extid)
        profile = self.get_profile(userid)
        if profile is None:
            profile = ValidatedDict()

        # Player scores for courses
        player_list = Node.void('player_list')
        data.add_child(player_list)
        player = Node.void('player')
        player_list.add_child(player)
        player.add_child(Node.s32('jid', extid))

        result_list = Node.void('result_list')
        player.add_child(result_list)
        playercourses = self.get_courses(userid)
        for courseid in playercourses:
            if courseid not in validcourses:
                continue

            rating = {
                self.COURSE_RATING_FAILED: self.GAME_COURSE_RATING_FAILED,
                self.COURSE_RATING_BRONZE: self.GAME_COURSE_RATING_BRONZE,
                self.COURSE_RATING_SILVER: self.GAME_COURSE_RATING_SILVER,
                self.COURSE_RATING_GOLD: self.GAME_COURSE_RATING_GOLD,
            }[playercourses[courseid]['rating']]
            scores = playercourses[courseid]['scores']

            result = Node.void('result')
            result_list.add_child(result)
            result.add_child(Node.s32('id', courseid))
            result.add_child(Node.u8('rating', rating))
            result.add_child(Node.s32_array('score', scores))

        # Last course ID
        last_course_id = Node.s32('last_course_id', profile.get_dict('last').get_int('last_course_id', -1))
        data.add_child(last_course_id)

        return gametop
Esempio n. 21
0
    def unformat_profile(self, userid: UserID, request: Node, oldprofile: ValidatedDict) -> ValidatedDict:
        newprofile = copy.deepcopy(oldprofile)
        data = request.child('data')

        # Grab player information
        player = data.child('player')

        # Grab last information. Lots of this will be filled in while grabbing scores
        last = newprofile.get_dict('last')
        last.replace_int('play_time', player.child_value('time_gameend'))
        last.replace_str('shopname', player.child_value('shopname'))
        last.replace_str('areaname', player.child_value('areaname'))

        # Grab player info for echoing back
        info = player.child('info')
        if info is not None:
            newprofile.replace_int('jubility', info.child_value('jubility'))
            newprofile.replace_int('jubility_yday', info.child_value('jubility_yday'))
            newprofile.replace_int('tune_cnt', info.child_value('tune_cnt'))
            newprofile.replace_int('save_cnt', info.child_value('save_cnt'))
            newprofile.replace_int('saved_cnt', info.child_value('saved_cnt'))
            newprofile.replace_int('fc_cnt', info.child_value('fc_cnt'))
            newprofile.replace_int('ex_cnt', info.child_value('exc_cnt'))  # Not a mistake, Jubeat is weird
            newprofile.replace_int('pf_cnt', info.child_value('pf_cnt'))
            newprofile.replace_int('clear_cnt', info.child_value('clear_cnt'))
            newprofile.replace_int('match_cnt', info.child_value('match_cnt'))
            newprofile.replace_int('beat_cnt', info.child_value('beat_cnt'))
            newprofile.replace_int('total_best_score', info.child_value('total_best_score'))
            newprofile.replace_int('mynews_cnt', info.child_value('mynews_cnt'))

        # Grab unlock progress
        item = player.child('item')
        if item is not None:
            newprofile.replace_int_array('secret_list', 32, item.child_value('secret_list'))
            newprofile.replace_int_array('title_list', 96, item.child_value('title_list'))
            newprofile.replace_int('theme_list', item.child_value('theme_list'))
            newprofile.replace_int_array('marker_list', 2, item.child_value('marker_list'))
            newprofile.replace_int_array('parts_list', 96, item.child_value('parts_list'))
            newprofile.replace_int_array('secret_list_new', 32, item.child_value('secret_new'))
            newprofile.replace_int_array('title_list_new', 96, item.child_value('title_new'))
            newprofile.replace_int('theme_list_new', item.child_value('theme_new'))
            newprofile.replace_int_array('marker_list_new', 2, item.child_value('marker_new'))

        # Grab bistro progress
        bistro = player.child('bistro')
        if bistro is not None:
            newprofile.replace_int('bistro_carry_over', bistro.child_value('carry_over'))

            chefdata = newprofile.get_dict('chef')
            chef = bistro.child('chef')
            if chef is not None:
                chefdata.replace_int('id', chef.child_value('id'))
                chefdata.replace_int('ability', chef.child_value('ability'))
                chefdata.replace_int('remain', chef.child_value('remain'))
                chefdata.replace_int('rate', chef.child_value('rate'))
            newprofile.replace_dict('chef', chefdata)

            for route in bistro.children:
                if route.name != 'route':
                    continue

                gourmates = route.child('gourmates')
                routeid = gourmates.child_value('id')
                satisfaction = gourmates.child_value('satisfaction_s32')
                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    routeid,
                    'route',
                    {
                        'satisfaction': satisfaction,
                    },
                )

        # Get timestamps for played songs
        timestamps: Dict[int, int] = {}
        history = player.child('history')
        if history is not None:
            for tune in history.children:
                if tune.name != 'tune':
                    continue
                entry = int(tune.attribute('log_id'))
                ts = int(tune.child_value('timestamp') / 1000)
                timestamps[entry] = ts

        # Grab scores and save those
        result = data.child('result')
        if result is not None:
            for tune in result.children:
                if tune.name != 'tune':
                    continue
                result = tune.child('player')

                last.replace_int('marker', tune.child_value('marker'))
                last.replace_int('title', tune.child_value('title'))
                last.replace_int('parts', tune.child_value('parts'))
                last.replace_int('theme', tune.child_value('theme'))
                last.replace_int('sort', tune.child_value('sort'))
                last.replace_int('category', tune.child_value('category'))
                last.replace_int('rank_sort', tune.child_value('rank_sort'))
                last.replace_int('combo_disp', tune.child_value('combo_disp'))

                songid = tune.child_value('music')
                entry = int(tune.attribute('id'))
                timestamp = timestamps.get(entry, Time.now())
                chart = int(result.child('score').attribute('seq'))
                points = result.child_value('score')
                flags = int(result.child('score').attribute('clear'))
                combo = int(result.child('score').attribute('combo'))
                ghost = result.child_value('mbar')

                # Miscelaneous last data for echoing to profile get
                last.replace_int('music_id', songid)
                last.replace_int('seq_id', chart)

                mapping = {
                    self.GAME_FLAG_BIT_CLEARED: self.PLAY_MEDAL_CLEARED,
                    self.GAME_FLAG_BIT_FULL_COMBO: self.PLAY_MEDAL_FULL_COMBO,
                    self.GAME_FLAG_BIT_EXCELLENT: self.PLAY_MEDAL_EXCELLENT,
                    self.GAME_FLAG_BIT_NEARLY_FULL_COMBO: self.PLAY_MEDAL_NEARLY_FULL_COMBO,
                    self.GAME_FLAG_BIT_NEARLY_EXCELLENT: self.PLAY_MEDAL_NEARLY_EXCELLENT,
                }

                # Figure out the highest medal based on bits passed in
                medal = self.PLAY_MEDAL_FAILED
                for bit in mapping:
                    if flags & bit > 0:
                        medal = max(medal, mapping[bit])

                self.update_score(userid, timestamp, songid, chart, points, medal, combo, ghost)

        # Save back last information gleaned from results
        newprofile.replace_dict('last', last)

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

        return newprofile
Esempio n. 22
0
    def unformat_profile(self, userid: UserID, request: Node,
                         oldprofile: ValidatedDict) -> ValidatedDict:
        newprofile = copy.deepcopy(oldprofile)

        # Set that we've seen this profile
        newprofile.replace_bool('welcom_pack', True)

        account = request.child('account')
        if account is not None:
            newprofile.replace_int('tutorial', account.child_value('tutorial'))
            newprofile.replace_int('read_news',
                                   account.child_value('read_news'))
            newprofile.replace_int('area_id', account.child_value('area_id'))
            newprofile.replace_int('lumina', account.child_value('lumina'))
            newprofile.replace_int_array('medal_set', 4,
                                         account.child_value('medal_set'))
            newprofile.replace_int_array('nice', 30,
                                         account.child_value('nice'))
            newprofile.replace_int_array('favorite_chara', 20,
                                         account.child_value('favorite_chara'))
            newprofile.replace_int_array('special_area', 8,
                                         account.child_value('special_area'))
            newprofile.replace_int_array(
                'chocolate_charalist', 5,
                account.child_value('chocolate_charalist'))
            newprofile.replace_int_array(
                'teacher_setting', 10, account.child_value('teacher_setting'))

        info = request.child('info')
        if info is not None:
            newprofile.replace_int('ep', info.child_value('ep'))

        config = request.child('config')
        if config is not None:
            newprofile.replace_int('mode', config.child_value('mode'))
            newprofile.replace_int('chara', config.child_value('chara'))
            newprofile.replace_int('music', config.child_value('music'))
            newprofile.replace_int('sheet', config.child_value('sheet'))
            newprofile.replace_int('category', config.child_value('category'))
            newprofile.replace_int('sub_category',
                                   config.child_value('sub_category'))
            newprofile.replace_int('chara_category',
                                   config.child_value('chara_category'))
            newprofile.replace_int('course_id',
                                   config.child_value('course_id'))
            newprofile.replace_int('course_folder',
                                   config.child_value('course_folder'))
            newprofile.replace_int('ms_banner_disp',
                                   config.child_value('ms_banner_disp'))
            newprofile.replace_int('ms_down_info',
                                   config.child_value('ms_down_info'))
            newprofile.replace_int('ms_side_info',
                                   config.child_value('ms_side_info'))
            newprofile.replace_int('ms_raise_type',
                                   config.child_value('ms_raise_type'))
            newprofile.replace_int('ms_rnd_type',
                                   config.child_value('ms_rnd_type'))

        option_dict = newprofile.get_dict('option')
        option = request.child('option')
        if option is not None:
            option_dict.replace_int('hispeed', option.child_value('hispeed'))
            option_dict.replace_int('popkun', option.child_value('popkun'))
            option_dict.replace_bool('hidden', option.child_value('hidden'))
            option_dict.replace_int('hidden_rate',
                                    option.child_value('hidden_rate'))
            option_dict.replace_bool('sudden', option.child_value('sudden'))
            option_dict.replace_int('sudden_rate',
                                    option.child_value('sudden_rate'))
            option_dict.replace_int('randmir', option.child_value('randmir'))
            option_dict.replace_int('gauge_type',
                                    option.child_value('gauge_type'))
            option_dict.replace_int('ojama_0', option.child_value('ojama_0'))
            option_dict.replace_int('ojama_1', option.child_value('ojama_1'))
            option_dict.replace_bool('forever_0',
                                     option.child_value('forever_0'))
            option_dict.replace_bool('forever_1',
                                     option.child_value('forever_1'))
            option_dict.replace_bool('full_setting',
                                     option.child_value('full_setting'))
            option_dict.replace_int('judge', option.child_value('judge'))
        newprofile.replace_dict('option', option_dict)

        customize = request.child('customize')
        if customize is not None:
            newprofile.replace_int('effect_left',
                                   customize.child_value('effect_left'))
            newprofile.replace_int('effect_center',
                                   customize.child_value('effect_center'))
            newprofile.replace_int('effect_right',
                                   customize.child_value('effect_right'))
            newprofile.replace_int('hukidashi',
                                   customize.child_value('hukidashi'))
            newprofile.replace_int('comment_1',
                                   customize.child_value('comment_1'))
            newprofile.replace_int('comment_2',
                                   customize.child_value('comment_2'))

        event = request.child('event')
        if event is not None:
            newprofile.replace_int('event_enemy_medal',
                                   event.child_value('enemy_medal'))
            newprofile.replace_int('event_hp', event.child_value('hp'))

        stamp = request.child('stamp')
        if stamp is not None:
            newprofile.replace_int('stamp_id', stamp.child_value('stamp_id'))
            newprofile.replace_int('stamp_cnt', stamp.child_value('cnt'))

        # Extract achievements
        for node in request.children:
            if node.name == 'item':
                itemid = node.child_value('id')
                itemtype = node.child_value('type')
                param = node.child_value('param')
                is_new = node.child_value('is_new')

                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    itemid,
                    f'item_{itemtype}',
                    {
                        'param': param,
                        'is_new': is_new,
                    },
                )

            elif node.name == 'chara_param':
                charaid = node.child_value('chara_id')
                friendship = node.child_value('friendship')

                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    charaid,
                    'chara',
                    {
                        'friendship': friendship,
                    },
                )

            elif node.name == 'medal':
                medalid = node.child_value('medal_id')
                level = node.child_value('level')
                exp = node.child_value('exp')
                set_count = node.child_value('set_count')
                get_count = node.child_value('get_count')

                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    medalid,
                    'medal',
                    {
                        'level': level,
                        'exp': exp,
                        'set_count': set_count,
                        'get_count': get_count,
                    },
                )

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

        return newprofile
Esempio n. 23
0
    def unformat_profile(self, userid: UserID, request: Node,
                         oldprofile: ValidatedDict) -> ValidatedDict:
        game_config = self.get_game_config()
        newprofile = copy.deepcopy(oldprofile)

        # Save base player profile info
        newprofile.replace_int(
            'lid',
            ID.parse_machine_id(request.child_value('pdata/account/lid')))
        newprofile.replace_str('name', request.child_value('pdata/base/name'))
        newprofile.replace_int('mg', request.child_value('pdata/base/mg'))
        newprofile.replace_int('ap', request.child_value('pdata/base/ap'))
        newprofile.replace_int('uattr',
                               request.child_value('pdata/base/uattr'))
        newprofile.replace_int('money',
                               request.child_value('pdata/base/money'))
        newprofile.replace_int('class',
                               request.child_value('pdata/base/class'))
        newprofile.replace_int('class_ar',
                               request.child_value('pdata/base/class_ar'))
        newprofile.replace_int('skill_point',
                               request.child_value('pdata/base/skill_point'))
        newprofile.replace_int('mgid',
                               request.child_value('pdata/minigame/mgid'))
        newprofile.replace_int('mgsc',
                               request.child_value('pdata/minigame/sc'))
        newprofile.replace_int_array(
            'favorites', 30, request.child_value('pdata/mylist/list/mlst'))

        # Save player config
        configdict = newprofile.get_dict('config')
        config = request.child('pdata/config')
        if config:
            configdict.replace_int('msel_bgm', config.child_value('msel_bgm'))
            configdict.replace_int('narrowdown_type',
                                   config.child_value('narrowdown_type'))
            configdict.replace_int('icon_id', config.child_value('icon_id'))
            configdict.replace_int('byword_0', config.child_value('byword_0'))
            configdict.replace_int('byword_1', config.child_value('byword_1'))
            configdict.replace_bool('is_auto_byword_0',
                                    config.child_value('is_auto_byword_0'))
            configdict.replace_bool('is_auto_byword_1',
                                    config.child_value('is_auto_byword_1'))
            configdict.replace_int('mrec_type',
                                   config.child_value('mrec_type'))
            configdict.replace_int('tab_sel', config.child_value('tab_sel'))
            configdict.replace_int('card_disp',
                                   config.child_value('card_disp'))
            configdict.replace_int('score_tab_disp',
                                   config.child_value('score_tab_disp'))
            configdict.replace_int('last_music_id',
                                   config.child_value('last_music_id'))
            configdict.replace_int('last_note_grade',
                                   config.child_value('last_note_grade'))
            configdict.replace_int('sort_type',
                                   config.child_value('sort_type'))
            configdict.replace_int('rival_panel_type',
                                   config.child_value('rival_panel_type'))
            configdict.replace_int('random_entry_work',
                                   config.child_value('random_entry_work'))
            configdict.replace_int('custom_folder_work',
                                   config.child_value('custom_folder_work'))
            configdict.replace_int('folder_type',
                                   config.child_value('folder_type'))
            configdict.replace_int('folder_lamp_type',
                                   config.child_value('folder_lamp_type'))
            configdict.replace_bool('is_tweet', config.child_value('is_tweet'))
            configdict.replace_bool('is_link_twitter',
                                    config.child_value('is_link_twitter'))
        newprofile.replace_dict('config', configdict)

        # Save player custom settings
        customdict = newprofile.get_dict('custom')
        custom = request.child('pdata/custom')
        if custom:
            customdict.replace_int('st_shot', custom.child_value('st_shot'))
            customdict.replace_int('st_frame', custom.child_value('st_frame'))
            customdict.replace_int('st_expl', custom.child_value('st_expl'))
            customdict.replace_int('st_bg', custom.child_value('st_bg'))
            customdict.replace_int('st_shot_vol',
                                   custom.child_value('st_shot_vol'))
            customdict.replace_int('st_bg_bri',
                                   custom.child_value('st_bg_bri'))
            customdict.replace_int('st_obj_size',
                                   custom.child_value('st_obj_size'))
            customdict.replace_int('st_jr_gauge',
                                   custom.child_value('st_jr_gauge'))
            customdict.replace_int('st_clr_gauge',
                                   custom.child_value('st_clr_gauge'))
            customdict.replace_int('st_gr_gauge_type',
                                   custom.child_value('st_gr_gauge_type'))
            customdict.replace_int('voice_message_set',
                                   custom.child_value('voice_message_set'))
            customdict.replace_int('same_time_note_disp',
                                   custom.child_value('same_time_note_disp'))
            customdict.replace_int('st_score_disp_type',
                                   custom.child_value('st_score_disp_type'))
            customdict.replace_int('st_bonus_type',
                                   custom.child_value('st_bonus_type'))
            customdict.replace_int('st_rivalnote_type',
                                   custom.child_value('st_rivalnote_type'))
            customdict.replace_int('st_topassist_type',
                                   custom.child_value('st_topassist_type'))
            customdict.replace_int('high_speed',
                                   custom.child_value('high_speed'))
            customdict.replace_int('st_hazard',
                                   custom.child_value('st_hazard'))
            customdict.replace_int('st_clr_cond',
                                   custom.child_value('st_clr_cond'))
            customdict.replace_int('voice_message_volume',
                                   custom.child_value('voice_message_volume'))
        newprofile.replace_dict('custom', customdict)

        # Save player parameter info
        params = request.child('pdata/player_param')
        if params:
            for child in params.children:
                if child.name != 'item':
                    continue

                item_type = child.child_value('type')
                bank = child.child_value('bank')
                data = child.child_value('data')
                while len(data) < 256:
                    data.append(0)
                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    bank,
                    f'player_param_{item_type}',
                    {
                        'data': data,
                    },
                )

        # Save player episode info
        episode = request.child('pdata/episode')
        if episode:
            for child in episode.children:
                if child.name != 'info':
                    continue

                # I assume this is copypasta, but I want to be sure
                extid = child.child_value('user_id')
                if extid != newprofile.get_int('extid'):
                    raise Exception(
                        f'Unexpected user ID, got {extid} expecting {newprofile.get_int("extid")}'
                    )

                episode_type = child.child_value('type')
                episode_value0 = child.child_value('value0')
                episode_value1 = child.child_value('value1')
                episode_text = child.child_value('text')
                episode_time = child.child_value('time')
                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    episode_type,
                    'episode',
                    {
                        'value0': episode_value0,
                        'value1': episode_value1,
                        'text': episode_text,
                        'time': episode_time,
                    },
                )

        # Save released info
        released = request.child('pdata/released')
        if released:
            for child in released.children:
                if child.name != 'info':
                    continue

                item_id = child.child_value('id')
                item_type = child.child_value('type')
                param = child.child_value('param')
                time = child.child_value('insert_time') or Time.now()
                if game_config.get_bool(
                        'force_unlock_songs') and item_type == 0:
                    # Don't save unlocks when we're force unlocking
                    continue

                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    item_id,
                    f'item_{item_type}',
                    {
                        'param': param,
                        'time': time,
                    },
                )

        # Save announce info
        announce = request.child('pdata/announce')
        if announce:
            for child in announce.children:
                if child.name != 'info':
                    continue

                announce_id = child.child_value('id')
                announce_type = child.child_value('type')
                param = child.child_value('param')
                need = child.child_value('bneedannounce')
                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    announce_id,
                    f'announcement_{announce_type}',
                    {
                        'param': param,
                        'need': need,
                    },
                )

        # Grab any new records set during this play session
        songplays = request.child('pdata/stglog')
        if songplays:
            for child in songplays.children:
                if child.name != 'log':
                    continue

                songid = child.child_value('mid')
                chart = child.child_value('ng')
                clear_type = child.child_value('ct')
                if songid == 0 and chart == 0 and clear_type == -1:
                    # Dummy song save during profile create
                    continue

                points = child.child_value('sc')
                achievement_rate = child.child_value('ar')
                param = child.child_value('param')
                miss_count = child.child_value('jt_ms')
                k_flag = child.child_value('k_flag')

                # Param is some random bits along with the combo type
                combo_type = param & 0x3
                param = param ^ combo_type

                clear_type = self._game_to_db_clear_type(clear_type)
                combo_type = self._game_to_db_combo_type(
                    combo_type, miss_count)
                self.update_score(
                    userid,
                    songid,
                    chart,
                    points,
                    achievement_rate,
                    clear_type,
                    combo_type,
                    miss_count,
                    param=param,
                    kflag=k_flag,
                )

        # Grab any new rivals added during this play session
        rivalnode = request.child('pdata/rival')
        if rivalnode:
            for child in rivalnode.children:
                if child.name != 'r':
                    continue

                extid = child.child_value('id')
                other_userid = self.data.remote.user.from_extid(
                    self.game, self.version, extid)
                if other_userid is None:
                    continue

                self.data.local.user.put_link(
                    self.game,
                    self.version,
                    userid,
                    'rival',
                    other_userid,
                    {},
                )

        # Save player dojo
        dojo = request.child('pdata/dojo')
        if dojo:
            dojoid = dojo.child_value('class')
            clear_type = dojo.child_value('clear_type')
            ar = dojo.child_value('t_ar')
            score = dojo.child_value('t_score')

            # Figure out timestamp stuff
            data = self.data.local.user.get_achievement(
                self.game,
                self.version,
                userid,
                dojoid,
                'dojo',
            ) or ValidatedDict()

            if ar >= data.get_int('ar'):
                # We set a new achievement rate, keep the new values
                record_time = Time.now()
            else:
                # We didn't, keep the old values for achievement rate, but
                # override score and clear_type only if they were better.
                record_time = data.get_int('record_timestamp')
                ar = data.get_int('ar')
                score = max(score, data.get_int('score'))
                clear_type = max(clear_type, data.get_int('clear_type'))

            play_time = Time.now()
            plays = data.get_int('plays') + 1

            self.data.local.user.put_achievement(
                self.game,
                self.version,
                userid,
                dojoid,
                'dojo',
                {
                    'clear_type': clear_type,
                    'ar': ar,
                    'score': score,
                    'plays': plays,
                    'play_timestamp': play_time,
                    'record_timestamp': record_time,
                },
            )

        # Save yurukome stuff
        yurukome_list = request.child('pdata/yurukome_list')
        if yurukome_list:
            for child in yurukome_list.children:
                if child.name != 'yurukome':
                    continue

                yurukome_id = child.child_value('yurukome_id')
                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    yurukome_id,
                    'yurukome',
                    {},
                )

        # Save mycourse stuff
        mycoursedict = newprofile.get_dict('mycourse')
        mycourse = request.child('pdata/mycourse')
        if mycourse:
            # Only replace course if it was a new record score-wise.
            score_1 = mycourse.child_value('score_1')
            score_2 = mycourse.child_value('score_2')
            score_3 = mycourse.child_value('score_3')
            score_4 = mycourse.child_value('score_4')
            total = 0
            for score in [score_1, score_2, score_3, score_4]:
                if score is not None and score >= 0:
                    total = total + score

            oldtotal = (mycoursedict.get_int('score_1', 0) +
                        mycoursedict.get_int('score_2', 0) +
                        mycoursedict.get_int('score_3', 0) +
                        mycoursedict.get_int('score_4', 0))

            if total >= oldtotal:
                mycoursedict.replace_int('music_id_1',
                                         mycourse.child_value('music_id_1'))
                mycoursedict.replace_int('note_grade_1',
                                         mycourse.child_value('note_grade_1'))
                mycoursedict.replace_int('score_1', score_1)
                mycoursedict.replace_int('music_id_2',
                                         mycourse.child_value('music_id_2'))
                mycoursedict.replace_int('note_grade_2',
                                         mycourse.child_value('note_grade_2'))
                mycoursedict.replace_int('score_2', score_2)
                mycoursedict.replace_int('music_id_3',
                                         mycourse.child_value('music_id_3'))
                mycoursedict.replace_int('note_grade_3',
                                         mycourse.child_value('note_grade_3'))
                mycoursedict.replace_int('score_3', score_3)
                mycoursedict.replace_int('music_id_4',
                                         mycourse.child_value('music_id_4'))
                mycoursedict.replace_int('note_grade_4',
                                         mycourse.child_value('note_grade_4'))
                mycoursedict.replace_int('score_4', score_4)
                mycoursedict.replace_int('insert_time', Time.now())
        newprofile.replace_dict('mycourse', mycoursedict)

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

        return newprofile
Esempio n. 24
0
    def unformat_profile(self, userid: UserID, request: Node,
                         oldprofile: ValidatedDict) -> ValidatedDict:
        newprofile = copy.deepcopy(oldprofile)

        account = request.child('account')
        if account is not None:
            newprofile.replace_int('tutorial', account.child_value('tutorial'))
            newprofile.replace_int('read_news',
                                   account.child_value('read_news'))

        info = request.child('info')
        if info is not None:
            newprofile.replace_int('ep', info.child_value('ep'))
            newprofile.replace_int('ap', info.child_value('ap'))

        config = request.child('config')
        if config is not None:
            newprofile.replace_int('mode', config.child_value('mode'))
            newprofile.replace_int('chara', config.child_value('chara'))
            newprofile.replace_int('music', config.child_value('music'))
            newprofile.replace_int('sheet', config.child_value('sheet'))
            newprofile.replace_int('category', config.child_value('category'))
            newprofile.replace_int('sub_category',
                                   config.child_value('sub_category'))
            newprofile.replace_int('chara_category',
                                   config.child_value('chara_category'))
            newprofile.replace_int('story_id', config.child_value('story_id'))
            newprofile.replace_int('course_id',
                                   config.child_value('course_id'))
            newprofile.replace_int('course_folder',
                                   config.child_value('course_folder'))
            newprofile.replace_int('story_folder',
                                   config.child_value('story_folder'))
            newprofile.replace_int('ms_banner_disp',
                                   config.child_value('ms_banner_disp'))
            newprofile.replace_int('ms_down_info',
                                   config.child_value('ms_down_info'))
            newprofile.replace_int('ms_side_info',
                                   config.child_value('ms_side_info'))
            newprofile.replace_int('ms_raise_type',
                                   config.child_value('ms_raise_type'))
            newprofile.replace_int('ms_rnd_type',
                                   config.child_value('ms_rnd_type'))

        option_dict = newprofile.get_dict('option')
        option = request.child('option')
        if option is not None:
            option_dict.replace_int('hispeed', option.child_value('hispeed'))
            option_dict.replace_int('popkun', option.child_value('popkun'))
            option_dict.replace_bool('hidden', option.child_value('hidden'))
            option_dict.replace_bool('sudden', option.child_value('sudden'))
            option_dict.replace_int('hidden_rate',
                                    option.child_value('hidden_rate'))
            option_dict.replace_int('sudden_rate',
                                    option.child_value('sudden_rate'))
            option_dict.replace_int('randmir', option.child_value('randmir'))
            option_dict.replace_int('gauge_type',
                                    option.child_value('gauge_type'))
            option_dict.replace_int('ojama_0', option.child_value('ojama_0'))
            option_dict.replace_int('ojama_1', option.child_value('ojama_1'))
            option_dict.replace_bool('forever_0',
                                     option.child_value('forever_0'))
            option_dict.replace_bool('forever_1',
                                     option.child_value('forever_1'))
            option_dict.replace_bool('full_setting',
                                     option.child_value('full_setting'))
        newprofile.replace_dict('option', option_dict)

        customize_dict = newprofile.get_dict('customize')
        customize = request.child('customize')
        if customize is not None:
            customize_dict.replace_int('effect',
                                       customize.child_value('effect'))
            customize_dict.replace_int('hukidashi',
                                       customize.child_value('hukidashi'))
            customize_dict.replace_int('font', customize.child_value('font'))
            customize_dict.replace_int('comment_1',
                                       customize.child_value('comment_1'))
            customize_dict.replace_int('comment_2',
                                       customize.child_value('comment_2'))
        newprofile.replace_dict('customize', customize_dict)

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

        # Extract achievements
        for node in request.children:
            if node.name == 'item':
                if not node.child_value('is_new'):
                    # No need to save this one
                    continue

                itemid = node.child_value('id')
                itemtype = node.child_value('type')
                param = node.child_value('param')

                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    itemid,
                    'item',
                    {
                        'type': itemtype,
                        'param': param,
                    },
                )

            elif node.name == 'achievement':
                achievementid = node.child_value('type')
                count = node.child_value('count')

                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    achievementid,
                    'achievement',
                    {
                        'count': count,
                    },
                )

            elif node.name == 'chara_param':
                charaid = node.child_value('chara_id')
                friendship = node.child_value('friendship')

                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    charaid,
                    'chara',
                    {
                        'friendship': friendship,
                    },
                )

            elif node.name == 'story':
                storyid = node.child_value('story_id')
                chapter = node.child_value('chapter_id')
                gauge = node.child_value('gauge_point')
                cleared = node.child_value('is_cleared')
                clear_chapter = node.child_value('clear_chapter')

                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    storyid,
                    'story',
                    {
                        'chapter': chapter,
                        'gauge': gauge,
                        'cleared': cleared,
                        'clear_chapter': clear_chapter,
                    },
                )

        return newprofile
Esempio n. 25
0
    def unformat_profile(self, userid: UserID, request: Node,
                         oldprofile: ValidatedDict) -> ValidatedDict:
        game_config = self.get_game_config()
        newprofile = copy.deepcopy(oldprofile)

        newprofile.replace_int('lid',
                               ID.parse_machine_id(request.child_value('lid')))
        newprofile.replace_str('name', request.child_value('pdata/base/name'))
        newprofile.replace_int('icon',
                               request.child_value('pdata/base/icon_id'))
        newprofile.replace_int('lvl', request.child_value('pdata/base/lv'))
        newprofile.replace_int('exp', request.child_value('pdata/base/exp'))
        newprofile.replace_int('mg', request.child_value('pdata/base/mg'))
        newprofile.replace_int('ap', request.child_value('pdata/base/ap'))
        newprofile.replace_int('pc', request.child_value('pdata/base/pc'))
        newprofile.replace_int('uattr',
                               request.child_value('pdata/base/uattr'))

        customdict = newprofile.get_dict('custom')
        custom = request.child('pdata/custom')
        if custom:
            customdict.replace_int('s_gls', custom.child_value('s_gls'))
            customdict.replace_int('bgm_m', custom.child_value('bgm_m'))
            customdict.replace_int('st_f', custom.child_value('st_f'))
            customdict.replace_int('st_bg', custom.child_value('st_bg'))
            customdict.replace_int('st_bg_b', custom.child_value('st_bg_b'))
            customdict.replace_int('eff_e', custom.child_value('eff_e'))
            customdict.replace_int('se_s', custom.child_value('se_s'))
            customdict.replace_int('se_s_v', custom.child_value('se_s_v'))
            customdict.replace_int('last_music_id',
                                   custom.child_value('last_music_id'))
            customdict.replace_int('last_note_grade',
                                   custom.child_value('last_note_grade'))
            customdict.replace_int('sort_type',
                                   custom.child_value('sort_type'))
            customdict.replace_int('narrowdown_type',
                                   custom.child_value('narrowdown_type'))
            customdict.replace_bool(
                'is_begginer', custom.child_value(
                    'is_begginer'))  # Yes, this is spelled right
            customdict.replace_bool('is_tut', custom.child_value('is_tut'))
            customdict.replace_int_array('symbol_chat_0', 6,
                                         custom.child_value('symbol_chat_0'))
            customdict.replace_int_array('symbol_chat_1', 6,
                                         custom.child_value('symbol_chat_1'))
            customdict.replace_int('gauge_style',
                                   custom.child_value('gauge_style'))
            customdict.replace_int('obj_shade',
                                   custom.child_value('obj_shade'))
            customdict.replace_int('obj_size', custom.child_value('obj_size'))
            customdict.replace_int_array('byword', 2,
                                         custom.child_value('byword'))
            customdict.replace_bool_array('is_auto_byword', 2,
                                          custom.child_value('is_auto_byword'))
            customdict.replace_bool('is_tweet', custom.child_value('is_tweet'))
            customdict.replace_bool('is_link_twitter',
                                    custom.child_value('is_link_twitter'))
            customdict.replace_int('mrec_type',
                                   custom.child_value('mrec_type'))
            customdict.replace_int('card_disp_type',
                                   custom.child_value('card_disp_type'))
            customdict.replace_int('tab_sel', custom.child_value('tab_sel'))
            customdict.replace_int_array('hidden_param', 20,
                                         custom.child_value('hidden_param'))
        newprofile.replace_dict('custom', customdict)

        # Music unlocks and other stuff
        released = request.child('pdata/released')
        if released:
            for child in released.children:
                if child.name != 'info':
                    continue

                item_id = child.child_value('id')
                item_type = child.child_value('type')
                param = child.child_value('param')
                if game_config.get_bool(
                        'force_unlock_songs') and item_type == 0:
                    # Don't save unlocks when we're force unlocking
                    continue

                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    item_id,
                    f'item_{item_type}',
                    {
                        'param': param,
                    },
                )

        # Grab any new records set during this play session. Reflec Beat Limelight only sends
        # the top record back for songs that were played at least once during the session.
        # Note that it sends the top record, so if you play the song twice, it will return
        # only one record. Also, if you get a lower score than a previous try, it will return
        # the previous try. So, we must also look at the battle log for the actual play scores,
        # and combine the data if we can.
        savedrecords: Dict[int, Dict[int, Dict[str, int]]] = {}
        songplays = request.child('pdata/record')
        if songplays:
            for child in songplays.children:
                if child.name != 'rec':
                    continue

                songid = child.child_value('mid')
                chart = child.child_value('ng')

                # These don't get sent with the battle logs, so we try to construct
                # the values here.
                if songid not in savedrecords:
                    savedrecords[songid] = {}
                savedrecords[songid][chart] = {
                    'achievement_rate': child.child_value('mrec_0/ar') * 10,
                    'points': child.child_value('mrec_0/bs'),
                    'combo': child.child_value('mrec_0/mc'),
                    'miss_count': child.child_value('mrec_0/bmc'),
                    'win': child.child_value('mrec_0/win'),
                    'lose': child.child_value('mrec_0/lose'),
                    'draw': child.child_value('mrec_0/draw'),
                    'earned_points': child.child_value('point'),
                }

        # Now, see the actual battles that were played. If we can, unify the data with a record.
        # We only do that when the record achievement rate and score matches the battle achievement
        # rate and score, so we know for a fact that that record was generated by this battle.
        battlelogs = request.child('pdata/blog')
        if battlelogs:
            for child in battlelogs.children:
                if child.name != 'log':
                    continue

                songid = child.child_value('mid')
                chart = child.child_value('ng')

                clear_type = child.child_value('myself/ct')
                achievement_rate = child.child_value('myself/ar') * 10
                points = child.child_value('myself/s')

                clear_type, combo_type = self.__game_to_db_clear_type(
                    clear_type)

                combo = None
                miss_count = -1
                stats = None

                if songid in savedrecords:
                    if chart in savedrecords[songid]:
                        data = savedrecords[songid][chart]

                        if (data['achievement_rate'] == achievement_rate
                                and data['points'] == points):
                            # This is the same record! Use the stats from it to update our
                            # internal representation.
                            combo = data['combo']
                            miss_count = data['miss_count']
                            stats = {
                                'win': data['win'],
                                'lose': data['lose'],
                                'draw': data['draw'],
                                'earned_points': data['earned_points'],
                            }

                self.update_score(
                    userid,
                    songid,
                    chart,
                    points,
                    achievement_rate,
                    clear_type,
                    combo_type,
                    miss_count,
                    combo=combo,
                    stats=stats,
                )

        # Keep track of glass points so unlocks work
        glass = request.child('pdata/glass')
        if glass:
            for child in glass.children:
                if child.name != 'g':
                    continue

                gid = child.child_value('id')
                exp = child.child_value('exp')
                self.data.local.user.put_achievement(
                    self.game,
                    self.version,
                    userid,
                    gid,
                    'glass',
                    {
                        'exp': exp,
                    },
                )

        # Keep track of favorite music selections
        fav_music_slot = request.child('pdata/fav_music_slot')
        if fav_music_slot:
            for child in fav_music_slot.children:
                if child.name != 'slot':
                    continue

                slot_id = child.child_value('slot_id')
                music_id = child.child_value('music_id')
                if music_id == -1:
                    # Delete this favorite
                    self.data.local.user.destroy_achievement(
                        self.game,
                        self.version,
                        userid,
                        slot_id,
                        'music',
                    )
                else:
                    # Add/update this favorite
                    self.data.local.user.put_achievement(
                        self.game,
                        self.version,
                        userid,
                        slot_id,
                        'music',
                        {
                            'music_id': music_id,
                        },
                    )

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

        return newprofile