Exemplo n.º 1
0
    def handle_pcbinfo_get_request(self, request: Node) -> Node:
        shop_id = ID.parse_machine_id(request.child_value('lid'))
        machine = self.get_machine_by_id(shop_id)
        if machine is not None:
            machine_name = machine.name
            close = machine.data.get_bool('close')
            hour = machine.data.get_int('hour')
            minute = machine.data.get_int('minute')
            pref = machine.data.get_int('pref', 51)
        else:
            machine_name = ''
            close = False
            hour = 0
            minute = 0
            pref = 51

        root = Node.void('pcbinfo')
        info = Node.void('info')
        root.add_child(info)

        info.add_child(Node.string('name', machine_name))
        info.add_child(Node.s16('pref', pref))
        info.add_child(Node.bool('close', close))
        info.add_child(Node.u8('hour', hour))
        info.add_child(Node.u8('min', minute))

        return root
Exemplo n.º 2
0
    def handle_game_3_new_request(self, request: Node) -> Node:
        refid = request.child_value('refid')
        name = request.child_value('name')
        loc = ID.parse_machine_id(request.child_value('locid'))
        self.new_profile_by_refid(refid, name, loc)

        root = Node.void('game_3')
        return root
Exemplo n.º 3
0
    def handle_pcb_rb5_pcb_boot_request(self, request: Node) -> Node:
        shop_id = ID.parse_machine_id(request.child_value('lid'))
        machine = self.get_machine_by_id(shop_id)
        if machine is not None:
            machine_name = machine.name
            close = machine.data.get_bool('close')
            hour = machine.data.get_int('hour')
            minute = machine.data.get_int('minute')
        else:
            machine_name = ''
            close = False
            hour = 0
            minute = 0

        root = Node.void('pcb')
        sinfo = Node.void('sinfo')
        root.add_child(sinfo)
        sinfo.add_child(Node.string('nm', machine_name))
        sinfo.add_child(Node.bool('cl_enbl', close))
        sinfo.add_child(Node.u8('cl_h', hour))
        sinfo.add_child(Node.u8('cl_m', minute))
        sinfo.add_child(Node.bool('shop_flag', True))
        return root
Exemplo n.º 4
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
Exemplo n.º 5
0
    def handle_game_3_hiscore_request(self, request: Node) -> Node:
        # Grab location for local scores
        locid = ID.parse_machine_id(request.child_value('locid'))

        # Start the response packet
        game = Node.void('game_3')

        # First, grab hit chart
        playcounts = self.data.local.music.get_hit_chart(
            self.game, self.version, 1024)

        hitchart = Node.void('hitchart')
        game.add_child(hitchart)
        for (songid, count) in playcounts:
            info = Node.void('info')
            hitchart.add_child(info)
            info.add_child(Node.u32('id', songid))
            info.add_child(Node.u32('cnt', count))

        # Now, grab user records
        records = self.data.remote.music.get_all_records(
            self.game, self.version)
        users = {
            uid: prof
            for (uid, prof) in self.get_any_profiles([r[0] for r in records])
        }

        hiscore_allover = Node.void('hiscore_allover')
        game.add_child(hiscore_allover)

        # Output records
        for (userid, score) in records:
            info = Node.void('info')

            if userid not in users:
                raise Exception(
                    'Logic error, could not find profile for user!')
            profile = users[userid]

            info.add_child(Node.u32('id', score.id))
            info.add_child(Node.u32('type', score.chart))
            info.add_child(Node.string('name', profile.get_str('name')))
            info.add_child(
                Node.string('seq', ID.format_extid(profile.get_int('extid'))))
            info.add_child(Node.u32('score', score.points))

            # Add to global scores
            hiscore_allover.add_child(info)

        # Now, grab local records
        area_users = [
            uid for (uid, prof) in self.data.local.user.get_all_profiles(
                self.game, self.version) if prof.get_int('loc', -1) == locid
        ]
        records = self.data.local.music.get_all_records(self.game,
                                                        self.version,
                                                        userlist=area_users)
        missing_players = [uid for (uid, _) in records if uid not in users]
        for (uid, prof) in self.get_any_profiles(missing_players):
            users[uid] = prof

        hiscore_location = Node.void('hiscore_location')
        game.add_child(hiscore_location)

        # Output records
        for (userid, score) in records:
            info = Node.void('info')

            if userid not in users:
                raise Exception(
                    'Logic error, could not find profile for user!')
            profile = users[userid]

            info.add_child(Node.u32('id', score.id))
            info.add_child(Node.u32('type', score.chart))
            info.add_child(Node.string('name', profile.get_str('name')))
            info.add_child(
                Node.string('seq', ID.format_extid(profile.get_int('extid'))))
            info.add_child(Node.u32('score', score.points))

            # Add to global scores
            hiscore_location.add_child(info)

        # Now, grab clear rates
        clear_rate = Node.void('clear_rate')
        game.add_child(clear_rate)

        clears = self.get_clear_rates()
        for songid in clears:
            for chart in clears[songid]:
                if clears[songid][chart]['total'] > 0:
                    rate = float(clears[songid][chart]['clears']) / float(
                        clears[songid][chart]['total'])
                    dnode = Node.void('d')
                    clear_rate.add_child(dnode)
                    dnode.add_child(Node.u32('id', songid))
                    dnode.add_child(Node.u32('type', chart))
                    dnode.add_child(Node.s16('cr', int(rate * 10000)))

        return game
Exemplo n.º 6
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
Exemplo n.º 7
0
    def handle_event_r_get_all_request(self, request: Node) -> Node:
        limit = request.child_value('limit')

        comments = [
            achievement for achievement in self.data.local.user.
            get_all_time_based_achievements(self.game, self.version)
            if achievement[1].type == 'puzzle_comment'
        ]
        comments.sort(key=lambda x: x[1].timestamp, reverse=True)
        statuses = self.data.local.lobby.get_all_play_session_infos(
            self.game, self.version)
        statuses.sort(key=lambda x: x[1]['time'], reverse=True)

        # Cap all comment blocks to the limit
        if limit >= 0:
            comments = comments[:limit]
            statuses = statuses[:limit]

        # Mapping of profiles to userIDs
        uid_mapping = {
            uid: prof
            for (uid, prof) in self.get_any_profiles([c[0] for c in comments] +
                                                     [s[0] for s in statuses])
        }

        # Mapping of location ID to machine name
        lid_mapping: Dict[int, str] = {}

        root = Node.void('event_r')
        root.add_child(Node.s32('time', Time.now()))
        statusnode = Node.void('status')
        root.add_child(statusnode)
        commentnode = Node.void('comment')
        root.add_child(commentnode)

        for (uid, comment) in comments:
            lid = ID.parse_machine_id(comment.data.get_str('lid'))

            # Look up external data for the request
            if lid not in lid_mapping:
                machine = self.get_machine_by_id(lid)
                if machine is not None:
                    lid_mapping[lid] = machine.name
                else:
                    lid_mapping[lid] = ''

            c = Node.void('c')
            commentnode.add_child(c)
            c.add_child(Node.s32('uid', uid_mapping[uid].get_int('extid')))
            c.add_child(Node.string('p_name',
                                    uid_mapping[uid].get_str('name')))
            c.add_child(Node.s32('exp', uid_mapping[uid].get_int('exp')))
            c.add_child(
                Node.s32('customize', comment.data.get_int('customize')))
            c.add_child(Node.s32('tid', comment.data.get_int('teamid')))
            c.add_child(Node.string('t_name',
                                    comment.data.get_str('teamname')))
            c.add_child(Node.string('lid', comment.data.get_str('lid')))
            c.add_child(Node.string('s_name', lid_mapping[lid]))
            c.add_child(Node.s8('pref', comment.data.get_int('prefecture')))
            c.add_child(Node.s32('time', comment.timestamp))
            c.add_child(Node.string('comment',
                                    comment.data.get_str('comment')))
            c.add_child(Node.bool('is_tweet', comment.data.get_bool('tweet')))

        for (uid, status) in statuses:
            lid = ID.parse_machine_id(status.get_str('lid'))

            # Look up external data for the request
            if lid not in lid_mapping:
                machine = self.get_machine_by_id(lid)
                if machine is not None:
                    lid_mapping[lid] = machine.name
                else:
                    lid_mapping[lid] = ''

            s = Node.void('s')
            statusnode.add_child(s)
            s.add_child(Node.s32('uid', uid_mapping[uid].get_int('extid')))
            s.add_child(Node.string('p_name',
                                    uid_mapping[uid].get_str('name')))
            s.add_child(Node.s32('exp', uid_mapping[uid].get_int('exp')))
            s.add_child(Node.s32('customize', status.get_int('customize')))
            s.add_child(
                Node.s32('tid', uid_mapping[uid].get_int('team_id', -1)))
            s.add_child(
                Node.string('t_name',
                            uid_mapping[uid].get_str('team_name', '')))
            s.add_child(Node.string('lid', status.get_str('lid')))
            s.add_child(Node.string('s_name', lid_mapping[lid]))
            s.add_child(Node.s8('pref', status.get_int('prefecture')))
            s.add_child(Node.s32('time', status.get_int('time')))
            s.add_child(Node.s8('status', status.get_int('status')))
            s.add_child(Node.s8('stage', status.get_int('stage')))
            s.add_child(Node.s32('mid', status.get_int('mid')))
            s.add_child(Node.s8('ng', status.get_int('ng')))

        return root
Exemplo n.º 8
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
Exemplo n.º 9
0
 def test_format_machine_id(self) -> None:
     self.assertEqual(ID.format_machine_id(123), 'US-123')
     self.assertEqual(ID.parse_machine_id('US-123'), 123)
     self.assertEqual(ID.parse_machine_id('bla'), None)
     self.assertEqual(ID.parse_machine_id('US-blah'), None)