def format_profile(self, profile: ValidatedDict, playstats: ValidatedDict) -> Dict[str, Any]: return { 'name': profile.get_str('name'), 'extid': ID.format_extid(profile.get_int('extid')), 'first_play_time': playstats.get_int('first_play_timestamp'), 'last_play_time': playstats.get_int('last_play_timestamp'), }
def format_profile(self, profile: ValidatedDict, playstats: ValidatedDict) -> Dict[str, Any]: name = 'なし' # Nothing shop = '未設定' # Not set shop_area = '未設定' # Not set for i in range(len(profile['strdatas'])): strdata = profile['strdatas'][i] # Figure out the profile type csvs = strdata.split(b',') if len(csvs) < 2: # Not long enough to care about continue datatype = csvs[1].decode('ascii') if datatype != 'IBBDAT00': # Not the right profile type requested continue name = self.__update_value(name, csvs[27]) shop = self.__update_value(shop, csvs[30]) shop_area = self.__update_value(shop_area, csvs[31]) return { 'name': name, 'extid': ID.format_extid(profile.get_int('extid')), 'shop': shop, 'shop_area': shop_area, 'first_play_time': playstats.get_int('first_play_timestamp'), 'last_play_time': playstats.get_int('last_play_timestamp'), 'plays': playstats.get_int('total_plays'), }
def __handle_usernew(self, userid: Optional[UserID], requestdata: Node, response: Node) -> None: if userid is None: raise Exception('Expecting valid UserID to create new profile!') machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) self.put_profile(userid, ValidatedDict({ 'lid': machine.id, })) profile = self.get_profile(userid) response.add_child(Node.string('seq', ID.format_extid(profile.get_int('extid')))) response.add_child(Node.s32('code', profile.get_int('extid'))) response.add_child(Node.string('shoparea', ''))
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: game = Node.void('game') # Generic profile stuff game.add_child(Node.string('name', profile.get_str('name'))) game.add_child( Node.string('code', ID.format_extid(profile.get_int('extid')))) game.add_child(Node.u32('gamecoin_packet', profile.get_int('packet'))) game.add_child(Node.u32('gamecoin_block', profile.get_int('block'))) game.add_child(Node.u32('exp_point', profile.get_int('exp'))) game.add_child(Node.u32('m_user_cnt', profile.get_int('m_user_cnt'))) game_config = self.get_game_config() if game_config.get_bool('force_unlock_cards'): game.add_child(Node.bool_array('have_item', [True] * 512)) else: game.add_child( Node.bool_array( 'have_item', [x > 0 for x in profile.get_int_array('have_item', 512)])) if game_config.get_bool('force_unlock_songs'): game.add_child(Node.bool_array('have_note', [True] * 512)) else: game.add_child( Node.bool_array( 'have_note', [x > 0 for x in profile.get_int_array('have_note', 512)])) # Last played stuff lastdict = profile.get_dict('last') last = Node.void('last') game.add_child(last) last.set_attribute('music_id', str(lastdict.get_int('music_id'))) last.set_attribute('music_type', str(lastdict.get_int('music_type'))) last.set_attribute('sort_type', str(lastdict.get_int('sort_type'))) last.set_attribute('headphone', str(lastdict.get_int('headphone'))) last.set_attribute('hispeed', str(lastdict.get_int('hispeed'))) last.set_attribute('appeal_id', str(lastdict.get_int('appeal_id'))) last.set_attribute('frame0', str(lastdict.get_int('frame0'))) last.set_attribute('frame1', str(lastdict.get_int('frame1'))) last.set_attribute('frame2', str(lastdict.get_int('frame2'))) last.set_attribute('frame3', str(lastdict.get_int('frame3'))) last.set_attribute('frame4', str(lastdict.get_int('frame4'))) return game
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('player22') # Result root.add_child(Node.s8('result', 0)) # Set up account account = Node.void('account') root.add_child(account) account.add_child(Node.string('name', profile.get_str('name', 'なし'))) account.add_child( Node.string('g_pm_id', ID.format_extid(profile.get_int('extid')))) account.add_child(Node.s8('tutorial', profile.get_int('tutorial', -1))) account.add_child( Node.s16('read_news', profile.get_int('read_news', 0))) account.add_child(Node.s8('staff', 0)) account.add_child(Node.s8('is_conv', 0)) account.add_child(Node.s16('item_type', 0)) account.add_child(Node.s16('item_id', 0)) account.add_child( Node.s16_array('license_data', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])) # Statistics section and scores section statistics = self.get_play_statistics(userid) last_play_date = statistics.get_int_array('last_play_date', 3) today_play_date = Time.todays_date() if (last_play_date[0] == today_play_date[0] and last_play_date[1] == today_play_date[1] and last_play_date[2] == today_play_date[2]): today_count = statistics.get_int('today_plays', 0) else: today_count = 0 account.add_child(Node.u8('active_fr_num', 0)) # TODO: Hook up rivals code? account.add_child( Node.s16('total_play_cnt', statistics.get_int('total_plays', 0))) account.add_child(Node.s16('today_play_cnt', today_count)) account.add_child( Node.s16('consecutive_days', statistics.get_int('consecutive_days', 0))) account.add_child( Node.s16('total_days', statistics.get_int('total_days', 0))) account.add_child(Node.s16('interval_day', 0)) # Add scores section last_played = [ x[0] for x in self.data.local.music.get_last_played( self.game, self.version, userid, 5) ] most_played = [ x[0] for x in self.data.local.music.get_most_played( self.game, self.version, userid, 10) ] while len(last_played) < 5: last_played.append(-1) while len(most_played) < 10: most_played.append(-1) account.add_child(Node.s16_array('my_best', most_played)) account.add_child(Node.s16_array('latest_music', last_played)) scores = self.data.remote.music.get_scores(self.game, self.version, userid) for score in scores: # Skip any scores for chart types we don't support if score.chart not in [ self.CHART_TYPE_EASY, self.CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER, self.CHART_TYPE_EX, ]: continue points = score.points medal = score.data.get_int('medal') music = Node.void('music') root.add_child(music) music.add_child(Node.s16('music_num', score.id)) music.add_child( Node.u8( 'sheet_num', { self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY, self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER, self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX, }[score.chart])) music.add_child(Node.s16('cnt', score.plays)) music.add_child(Node.s32('score', points)) music.add_child( Node.u8( 'clear_type', { self.PLAY_MEDAL_CIRCLE_FAILED: self.GAME_PLAY_MEDAL_CIRCLE_FAILED, self.PLAY_MEDAL_DIAMOND_FAILED: self.GAME_PLAY_MEDAL_DIAMOND_FAILED, self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED, self.PLAY_MEDAL_EASY_CLEAR: self.GAME_PLAY_MEDAL_EASY_CLEAR, self.PLAY_MEDAL_CIRCLE_CLEARED: self.GAME_PLAY_MEDAL_CIRCLE_CLEARED, self.PLAY_MEDAL_DIAMOND_CLEARED: self.GAME_PLAY_MEDAL_DIAMOND_CLEARED, self.PLAY_MEDAL_STAR_CLEARED: self.GAME_PLAY_MEDAL_STAR_CLEARED, self.PLAY_MEDAL_CIRCLE_FULL_COMBO: self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO, self.PLAY_MEDAL_DIAMOND_FULL_COMBO: self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO, self.PLAY_MEDAL_STAR_FULL_COMBO: self.GAME_PLAY_MEDAL_STAR_FULL_COMBO, self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT, }[medal])) music.add_child(Node.s32('old_score', 0)) music.add_child(Node.u8('old_clear_type', 0)) # Net VS section netvs = Node.void('netvs') root.add_child(netvs) netvs.add_child(Node.s32('rank_point', 0)) netvs.add_child(Node.s16_array('record', [0, 0, 0, 0, 0, 0])) netvs.add_child(Node.u8('rank', 0)) netvs.add_child(Node.s8('vs_rank_old', 0)) netvs.add_child(Node.s8_array('ojama_condition', [0] * 74)) netvs.add_child(Node.s8_array('set_ojama', [0, 0, 0])) netvs.add_child(Node.s8_array('set_recommend', [0, 0, 0])) netvs.add_child(Node.u32('netvs_play_cnt', 0)) for dialog in [0, 1, 2, 3, 4, 5]: # TODO: Configure this, maybe? netvs.add_child(Node.string('dialog', f'dialog#{dialog}')) # Set up config config = Node.void('config') root.add_child(config) config.add_child(Node.u8('mode', profile.get_int('mode', 0))) config.add_child(Node.s16('chara', profile.get_int('chara', -1))) config.add_child(Node.s16('music', profile.get_int('music', -1))) config.add_child(Node.u8('sheet', profile.get_int('sheet', 0))) config.add_child(Node.s8('category', profile.get_int('category', 1))) config.add_child( Node.s8('sub_category', profile.get_int('sub_category', -1))) config.add_child( Node.s8('chara_category', profile.get_int('chara_category', -1))) config.add_child(Node.s16('story_id', profile.get_int('story_id', -1))) config.add_child( Node.s16('course_id', profile.get_int('course_id', -1))) config.add_child( Node.s8('course_folder', profile.get_int('course_folder', -1))) config.add_child( Node.s8('story_folder', profile.get_int('story_folder', -1))) config.add_child( Node.s8('ms_banner_disp', profile.get_int('ms_banner_disp'))) config.add_child( Node.s8('ms_down_info', profile.get_int('ms_down_info'))) config.add_child( Node.s8('ms_side_info', profile.get_int('ms_side_info'))) config.add_child( Node.s8('ms_raise_type', profile.get_int('ms_raise_type'))) config.add_child(Node.s8('ms_rnd_type', profile.get_int('ms_rnd_type'))) # Set up option option_dict = profile.get_dict('option') option = Node.void('option') root.add_child(option) option.add_child( Node.s16('hispeed', option_dict.get_int('hispeed', 10))) option.add_child(Node.u8('popkun', option_dict.get_int('popkun', 0))) option.add_child( Node.bool('hidden', option_dict.get_bool('hidden', False))) option.add_child( Node.s16('hidden_rate', option_dict.get_int('hidden_rate', -1))) option.add_child( Node.bool('sudden', option_dict.get_bool('sudden', False))) option.add_child( Node.s16('sudden_rate', option_dict.get_int('sudden_rate', -1))) option.add_child(Node.s8('randmir', option_dict.get_int('randmir', 0))) option.add_child( Node.s8('gauge_type', option_dict.get_int('gauge_type', 0))) option.add_child(Node.u8('ojama_0', option_dict.get_int('ojama_0', 0))) option.add_child(Node.u8('ojama_1', option_dict.get_int('ojama_1', 0))) option.add_child( Node.bool('forever_0', option_dict.get_bool('forever_0', False))) option.add_child( Node.bool('forever_1', option_dict.get_bool('forever_1', False))) option.add_child( Node.bool('full_setting', option_dict.get_bool('full_setting', False))) # Set up info info = Node.void('info') root.add_child(info) info.add_child(Node.u16('ep', profile.get_int('ep', 0))) info.add_child(Node.u16('ap', profile.get_int('ap', 0))) # Set up custom_cate custom_cate = Node.void('custom_cate') root.add_child(custom_cate) custom_cate.add_child(Node.s8('valid', 0)) custom_cate.add_child(Node.s8('lv_min', -1)) custom_cate.add_child(Node.s8('lv_max', -1)) custom_cate.add_child(Node.s8('medal_min', -1)) custom_cate.add_child(Node.s8('medal_max', -1)) custom_cate.add_child(Node.s8('friend_no', -1)) custom_cate.add_child(Node.s8('score_flg', -1)) # Set up customize customize_dict = profile.get_dict('customize') customize = Node.void('customize') root.add_child(customize) customize.add_child( Node.u16('effect', customize_dict.get_int('effect'))) customize.add_child( Node.u16('hukidashi', customize_dict.get_int('hukidashi'))) customize.add_child(Node.u16('font', customize_dict.get_int('font'))) customize.add_child( Node.u16('comment_1', customize_dict.get_int('comment_1'))) customize.add_child( Node.u16('comment_2', customize_dict.get_int('comment_2'))) # Set up achievements achievements = self.data.local.user.get_achievements( self.game, self.version, userid) for achievement in achievements: if achievement.type == 'item': itemtype = achievement.data.get_int('type') param = achievement.data.get_int('param') item = Node.void('item') root.add_child(item) item.add_child(Node.u8('type', itemtype)) item.add_child(Node.u16('id', achievement.id)) item.add_child(Node.u16('param', param)) item.add_child(Node.bool('is_new', False)) elif achievement.type == 'achievement': count = achievement.data.get_int('count') ach_node = Node.void('achievement') root.add_child(ach_node) ach_node.add_child(Node.u8('type', achievement.id)) ach_node.add_child(Node.u32('count', count)) elif achievement.type == 'chara': friendship = achievement.data.get_int('friendship') chara = Node.void('chara_param') root.add_child(chara) chara.add_child(Node.u16('chara_id', achievement.id)) chara.add_child(Node.u16('friendship', friendship)) elif achievement.type == 'story': chapter = achievement.data.get_int('chapter') gauge = achievement.data.get_int('gauge') cleared = achievement.data.get_bool('cleared') clear_chapter = achievement.data.get_int('clear_chapter') story = Node.void('story') root.add_child(story) story.add_child(Node.u32('story_id', achievement.id)) story.add_child(Node.u32('chapter_id', chapter)) story.add_child(Node.u16('gauge_point', gauge)) story.add_child(Node.bool('is_cleared', cleared)) story.add_child(Node.u32('clear_chapter', clear_chapter)) return root
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
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: game = Node.void('game_3') # Generic profile stuff game.add_child(Node.string('name', profile.get_str('name'))) game.add_child(Node.string('code', ID.format_extid(profile.get_int('extid')))) game.add_child(Node.u32('gamecoin_packet', profile.get_int('packet'))) game.add_child(Node.u32('gamecoin_block', profile.get_int('block'))) game.add_child(Node.s16('skill_name_id', profile.get_int('skill_name_id', -1))) game.add_child(Node.s32_array('hidden_param', profile.get_int_array('hidden_param', 20))) game.add_child(Node.u32('blaster_energy', profile.get_int('blaster_energy'))) game.add_child(Node.u32('blaster_count', profile.get_int('blaster_count'))) # Enable Ryusei Festa ryusei_festa = Node.void('ryusei_festa') game.add_child(ryusei_festa) ryusei_festa.add_child(Node.bool('ryusei_festa_trigger', True)) # Play statistics statistics = self.get_play_statistics(userid) last_play_date = statistics.get_int_array('last_play_date', 3) today_play_date = Time.todays_date() if ( last_play_date[0] == today_play_date[0] and last_play_date[1] == today_play_date[1] and last_play_date[2] == today_play_date[2] ): today_count = statistics.get_int('today_plays', 0) else: today_count = 0 game.add_child(Node.u32('play_count', statistics.get_int('total_plays', 0))) game.add_child(Node.u32('daily_count', today_count)) game.add_child(Node.u32('play_chain', statistics.get_int('consecutive_days', 0))) # Last played stuff if 'last' in profile: lastdict = profile.get_dict('last') last = Node.void('last') game.add_child(last) last.add_child(Node.s32('music_id', lastdict.get_int('music_id', -1))) last.add_child(Node.u8('music_type', lastdict.get_int('music_type'))) last.add_child(Node.u8('sort_type', lastdict.get_int('sort_type'))) last.add_child(Node.u8('narrow_down', lastdict.get_int('narrow_down'))) last.add_child(Node.u8('headphone', lastdict.get_int('headphone'))) last.add_child(Node.u16('appeal_id', lastdict.get_int('appeal_id', 1001))) last.add_child(Node.u16('comment_id', lastdict.get_int('comment_id'))) last.add_child(Node.u8('gauge_option', lastdict.get_int('gauge_option'))) # Item unlocks itemnode = Node.void('item') game.add_child(itemnode) game_config = self.get_game_config() achievements = self.data.local.user.get_achievements(self.game, self.version, userid) for item in achievements: if item.type[:5] != 'item_': continue itemtype = int(item.type[5:]) if game_config.get_bool('force_unlock_songs') and itemtype == self.GAME_CATALOG_TYPE_SONG: # Don't echo unlocked songs, we will add all of them later continue info = Node.void('info') itemnode.add_child(info) info.add_child(Node.u8('type', itemtype)) info.add_child(Node.u32('id', item.id)) info.add_child(Node.u32('param', item.data.get_int('param'))) if 'diff_param' in item.data: info.add_child(Node.s32('diff_param', item.data.get_int('diff_param'))) if game_config.get_bool('force_unlock_songs'): ids: Dict[int, int] = {} songs = self.data.local.music.get_all_songs(self.game, self.version) for song in songs: if song.id not in ids: ids[song.id] = 0 if song.data.get_int('difficulty') > 0: ids[song.id] = ids[song.id] | (1 << song.chart) for itemid in ids: if ids[itemid] == 0: continue info = Node.void('info') itemnode.add_child(info) info.add_child(Node.u8('type', self.GAME_CATALOG_TYPE_SONG)) info.add_child(Node.u32('id', itemid)) info.add_child(Node.u32('param', ids[itemid])) return game
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('playerdata') # Set up the base profile base = Node.void('base') root.add_child(base) base.add_child(Node.string('name', profile.get_str('name', 'なし'))) base.add_child( Node.string('g_pm_id', ID.format_extid(profile.get_int('extid')))) base.add_child(Node.u8('mode', profile.get_int('mode', 0))) base.add_child(Node.s8('button', profile.get_int('button', 0))) base.add_child( Node.s8('last_play_flag', profile.get_int('last_play_flag', -1))) base.add_child( Node.u8('medal_and_friend', profile.get_int('medal_and_friend', 0))) base.add_child(Node.s8('category', profile.get_int('category', -1))) base.add_child( Node.s8('sub_category', profile.get_int('sub_category', -1))) base.add_child(Node.s16('chara', profile.get_int('chara', -1))) base.add_child( Node.s8('chara_category', profile.get_int('chara_category', -1))) base.add_child(Node.u8('collabo', profile.get_int('collabo', 255))) base.add_child(Node.u8('sheet', profile.get_int('sheet', 0))) base.add_child(Node.s8('tutorial', profile.get_int('tutorial', 0))) base.add_child( Node.s32('music_open_pt', profile.get_int('music_open_pt', 0))) base.add_child(Node.s8('is_conv', -1)) base.add_child(Node.s32('option', profile.get_int('option', 0))) base.add_child(Node.s16('music', profile.get_int('music', -1))) base.add_child(Node.u16('ep', profile.get_int('ep', 0))) base.add_child( Node.s32_array('sp_color_flg', profile.get_int_array('sp_color_flg', 2))) base.add_child(Node.s32('read_news', profile.get_int('read_news', 0))) base.add_child( Node.s16('consecutive_days_coupon', profile.get_int('consecutive_days_coupon', 0))) base.add_child(Node.s8('staff', 0)) # Player card section player_card_dict = profile.get_dict('player_card') player_card = Node.void('player_card') root.add_child(player_card) player_card.add_child( Node.u8_array('title', player_card_dict.get_int_array('title', 2, [0, 1]))) player_card.add_child( Node.u8('frame', player_card_dict.get_int('frame'))) player_card.add_child(Node.u8('base', player_card_dict.get_int('base'))) player_card.add_child( Node.u8_array('seal', player_card_dict.get_int_array('seal', 2))) player_card.add_child( Node.s32_array('get_title', player_card_dict.get_int_array('get_title', 4))) player_card.add_child( Node.s32('get_frame', player_card_dict.get_int('get_frame'))) player_card.add_child( Node.s32('get_base', player_card_dict.get_int('get_base'))) player_card.add_child( Node.s32_array('get_seal', player_card_dict.get_int_array('get_seal', 2))) # Player card EX section player_card_ex = Node.void('player_card_ex') root.add_child(player_card_ex) player_card_ex.add_child( Node.s32('get_title_ex', player_card_dict.get_int('get_title_ex'))) player_card_ex.add_child( Node.s32('get_frame_ex', player_card_dict.get_int('get_frame_ex'))) player_card_ex.add_child( Node.s32('get_base_ex', player_card_dict.get_int('get_base_ex'))) player_card_ex.add_child( Node.s32('get_seal_ex', player_card_dict.get_int('get_seal_ex'))) # Statistics section and scores section statistics = self.get_play_statistics(userid) last_play_date = statistics.get_int_array('last_play_date', 3) today_play_date = Time.todays_date() if (last_play_date[0] == today_play_date[0] and last_play_date[1] == today_play_date[1] and last_play_date[2] == today_play_date[2]): today_count = statistics.get_int('today_plays', 0) else: today_count = 0 base.add_child(Node.u8('active_fr_num', 0)) # TODO: Hook up rivals code? base.add_child( Node.s32('total_play_cnt', statistics.get_int('total_plays', 0))) base.add_child(Node.s16('today_play_cnt', today_count)) base.add_child( Node.s16('consecutive_days', statistics.get_int('consecutive_days', 0))) last_played = [ x[0] for x in self.data.local.music.get_last_played( self.game, self.version, userid, 3) ] most_played = [ x[0] for x in self.data.local.music.get_most_played( self.game, self.version, userid, 20) ] while len(last_played) < 3: last_played.append(-1) while len(most_played) < 20: most_played.append(-1) hiscore_array = [0] * int( (((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8) clear_medal = [0] * self.GAME_MAX_MUSIC_ID clear_medal_sub = [0] * self.GAME_MAX_MUSIC_ID scores = self.data.remote.music.get_scores(self.game, self.version, userid) for score in scores: if score.id > self.GAME_MAX_MUSIC_ID: continue # Skip any scores for chart types we don't support if score.chart not in [ self.CHART_TYPE_EASY, self.CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER, self.CHART_TYPE_EX, ]: continue points = score.points clear_medal[score.id] = clear_medal[ score.id] | self.__format_medal_for_score(score) hiscore_index = (score.id * 4) + { self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION, self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION, self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER_POSITION, self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX_POSITION, }[score.chart] hiscore_byte_pos = int((hiscore_index * 17) / 8) hiscore_bit_pos = int((hiscore_index * 17) % 8) hiscore_value = points << hiscore_bit_pos hiscore_array[hiscore_byte_pos] = hiscore_array[ hiscore_byte_pos] | (hiscore_value & 0xFF) hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ( (hiscore_value >> 8) & 0xFF) hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ( (hiscore_value >> 16) & 0xFF) hiscore = bytes(hiscore_array) player_card.add_child(Node.s16_array('best_music', most_played[0:3])) base.add_child(Node.s16_array('my_best', most_played)) base.add_child(Node.s16_array('latest_music', last_played)) base.add_child(Node.u16_array('clear_medal', clear_medal)) base.add_child(Node.u8_array('clear_medal_sub', clear_medal_sub)) # Goes outside of base for some reason root.add_child(Node.binary('hiscore', hiscore)) # Net VS section netvs = Node.void('netvs') root.add_child(netvs) netvs.add_child(Node.s32_array('get_ojama', [0, 0])) netvs.add_child(Node.s32('rank_point', 0)) netvs.add_child(Node.s32('play_point', 0)) netvs.add_child(Node.s16_array('record', [0, 0, 0, 0, 0, 0])) netvs.add_child(Node.u8('rank', 0)) netvs.add_child(Node.s8_array('ojama_condition', [0] * 74)) netvs.add_child(Node.s8_array('set_ojama', [0, 0, 0])) netvs.add_child(Node.s8_array('set_recommend', [0, 0, 0])) netvs.add_child(Node.s8_array('jewelry', [0] * 15)) for dialog in [0, 1, 2, 3, 4, 5]: # TODO: Configure this, maybe? netvs.add_child(Node.string('dialog', 'dialog#{}'.format(dialog))) sp_data = Node.void('sp_data') root.add_child(sp_data) sp_data.add_child(Node.s32('sp', profile.get_int('sp', 0))) reflec_data = Node.void('reflec_data') root.add_child(reflec_data) reflec_data.add_child( Node.s8_array('reflec', profile.get_int_array('reflec', 2))) # Navigate section navigate_dict = profile.get_dict('navigate') navigate = Node.void('navigate') root.add_child(navigate) navigate.add_child(Node.s8('genre', navigate_dict.get_int('genre'))) navigate.add_child(Node.s8('image', navigate_dict.get_int('image'))) navigate.add_child(Node.s8('level', navigate_dict.get_int('level'))) navigate.add_child(Node.s8('ojama', navigate_dict.get_int('ojama'))) navigate.add_child( Node.s16('limit_num', navigate_dict.get_int('limit_num'))) navigate.add_child(Node.s8('button', navigate_dict.get_int('button'))) navigate.add_child(Node.s8('life', navigate_dict.get_int('life'))) navigate.add_child( Node.s16('progress', navigate_dict.get_int('progress'))) return root
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('playerdata') # Set up the base profile base = Node.void('base') root.add_child(base) base.add_child(Node.string('name', profile.get_str('name', 'なし'))) base.add_child( Node.string('g_pm_id', ID.format_extid(profile.get_int('extid')))) base.add_child(Node.u8('mode', profile.get_int('mode', 0))) base.add_child(Node.s8('button', profile.get_int('button', 0))) base.add_child( Node.s8('last_play_flag', profile.get_int('last_play_flag', -1))) base.add_child( Node.u8('medal_and_friend', profile.get_int('medal_and_friend', 0))) base.add_child(Node.s8('category', profile.get_int('category', -1))) base.add_child( Node.s8('sub_category', profile.get_int('sub_category', -1))) base.add_child(Node.s16('chara', profile.get_int('chara', -1))) base.add_child( Node.s8('chara_category', profile.get_int('chara_category', -1))) base.add_child(Node.u8('collabo', 255)) base.add_child(Node.u8('sheet', profile.get_int('sheet', 0))) base.add_child(Node.s8('tutorial', profile.get_int('tutorial', 0))) base.add_child( Node.s8('music_open_pt', profile.get_int('music_open_pt', 0))) base.add_child(Node.s8('is_conv', -1)) base.add_child(Node.s32('option', profile.get_int('option', 0))) base.add_child(Node.s16('music', profile.get_int('music', -1))) base.add_child(Node.u16('ep', profile.get_int('ep', 0))) base.add_child( Node.s32_array('sp_color_flg', profile.get_int_array('sp_color_flg', 2))) base.add_child(Node.s32('read_news', profile.get_int('read_news', 0))) base.add_child( Node.s16('consecutive_days_coupon', profile.get_int('consecutive_days_coupon', 0))) base.add_child(Node.s8('staff', 0)) # These are probably from an old event, but if they aren't present and defaulted, # then different songs show up in the Zoo event. base.add_child( Node.u16_array( 'gitadora_point', profile.get_int_array('gitadora_point', 3, [2000, 2000, 2000]))) base.add_child( Node.u8('gitadora_select', profile.get_int('gitadora_select', 2))) # Statistics section and scores section statistics = self.get_play_statistics(userid) last_play_date = statistics.get_int_array('last_play_date', 3) today_play_date = Time.todays_date() if (last_play_date[0] == today_play_date[0] and last_play_date[1] == today_play_date[1] and last_play_date[2] == today_play_date[2]): today_count = statistics.get_int('today_plays', 0) else: today_count = 0 base.add_child(Node.u8('active_fr_num', 0)) # TODO: Hook up rivals code? base.add_child( Node.s32('total_play_cnt', statistics.get_int('total_plays', 0))) base.add_child(Node.s16('today_play_cnt', today_count)) base.add_child( Node.s16('consecutive_days', statistics.get_int('consecutive_days', 0))) last_played = [ x[0] for x in self.data.local.music.get_last_played( self.game, self.version, userid, 3) ] most_played = [ x[0] for x in self.data.local.music.get_most_played( self.game, self.version, userid, 20) ] while len(last_played) < 3: last_played.append(-1) while len(most_played) < 20: most_played.append(-1) hiscore_array = [0] * int( (((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8) clear_medal = [0] * self.GAME_MAX_MUSIC_ID clear_medal_sub = [0] * self.GAME_MAX_MUSIC_ID scores = self.data.remote.music.get_scores(self.game, self.version, userid) for score in scores: if score.id > self.GAME_MAX_MUSIC_ID: continue # Skip any scores for chart types we don't support if score.chart not in [ self.CHART_TYPE_EASY, self.CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER, self.CHART_TYPE_EX, ]: continue points = score.points medal = { self.PLAY_MEDAL_CIRCLE_FAILED: self.GAME_PLAY_MEDAL_CIRCLE_FAILED, self.PLAY_MEDAL_DIAMOND_FAILED: self.GAME_PLAY_MEDAL_DIAMOND_FAILED, self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED, self.PLAY_MEDAL_EASY_CLEAR: self.GAME_PLAY_MEDAL_CIRCLE_CLEARED, # Map approximately self.PLAY_MEDAL_CIRCLE_CLEARED: self.GAME_PLAY_MEDAL_CIRCLE_CLEARED, self.PLAY_MEDAL_DIAMOND_CLEARED: self.GAME_PLAY_MEDAL_DIAMOND_CLEARED, self.PLAY_MEDAL_STAR_CLEARED: self.GAME_PLAY_MEDAL_STAR_CLEARED, self.PLAY_MEDAL_CIRCLE_FULL_COMBO: self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO, self.PLAY_MEDAL_DIAMOND_FULL_COMBO: self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO, self.PLAY_MEDAL_STAR_FULL_COMBO: self.GAME_PLAY_MEDAL_STAR_FULL_COMBO, self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT, }[score.data.get_int('medal')] clear_medal[score.id] = clear_medal[score.id] | (medal << (score.chart * 4)) hiscore_index = (score.id * 4) + { self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION, self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION, self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER_POSITION, self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX_POSITION, }[score.chart] hiscore_byte_pos = int((hiscore_index * 17) / 8) hiscore_bit_pos = int((hiscore_index * 17) % 8) hiscore_value = points << hiscore_bit_pos hiscore_array[hiscore_byte_pos] = hiscore_array[ hiscore_byte_pos] | (hiscore_value & 0xFF) hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ( (hiscore_value >> 8) & 0xFF) hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ( (hiscore_value >> 16) & 0xFF) hiscore = bytes(hiscore_array) base.add_child(Node.s16_array('my_best', most_played)) base.add_child(Node.s16_array('latest_music', last_played)) base.add_child(Node.u16_array('clear_medal', clear_medal)) base.add_child(Node.u8_array('clear_medal_sub', clear_medal_sub)) # Goes outside of base for some reason root.add_child(Node.binary('hiscore', hiscore)) # Avatar section avatar_dict = profile.get_dict('avatar') avatar = Node.void('avatar') root.add_child(avatar) avatar.add_child(Node.u8('hair', avatar_dict.get_int('hair', 0))) avatar.add_child(Node.u8('face', avatar_dict.get_int('face', 0))) avatar.add_child(Node.u8('body', avatar_dict.get_int('body', 0))) avatar.add_child(Node.u8('effect', avatar_dict.get_int('effect', 0))) avatar.add_child(Node.u8('object', avatar_dict.get_int('object', 0))) avatar.add_child( Node.u8_array('comment', avatar_dict.get_int_array('comment', 2))) avatar.add_child( Node.s32_array('get_hair', avatar_dict.get_int_array('get_hair', 2))) avatar.add_child( Node.s32_array('get_face', avatar_dict.get_int_array('get_face', 2))) avatar.add_child( Node.s32_array('get_body', avatar_dict.get_int_array('get_body', 2))) avatar.add_child( Node.s32_array('get_effect', avatar_dict.get_int_array('get_effect', 2))) avatar.add_child( Node.s32_array('get_object', avatar_dict.get_int_array('get_object', 2))) avatar.add_child( Node.s32_array('get_comment_over', avatar_dict.get_int_array('get_comment_over', 3))) avatar.add_child( Node.s32_array('get_comment_under', avatar_dict.get_int_array('get_comment_under', 3))) # Avatar add section avatar_add_dict = profile.get_dict('avatar_add') avatar_add = Node.void('avatar_add') root.add_child(avatar_add) avatar_add.add_child( Node.s32_array('get_hair', avatar_add_dict.get_int_array('get_hair', 2))) avatar_add.add_child( Node.s32_array('get_face', avatar_add_dict.get_int_array('get_face', 2))) avatar_add.add_child( Node.s32_array('get_body', avatar_add_dict.get_int_array('get_body', 2))) avatar_add.add_child( Node.s32_array('get_effect', avatar_add_dict.get_int_array('get_effect', 2))) avatar_add.add_child( Node.s32_array('get_object', avatar_add_dict.get_int_array('get_object', 2))) avatar_add.add_child( Node.s32_array( 'get_comment_over', avatar_add_dict.get_int_array('get_comment_over', 2))) avatar_add.add_child( Node.s32_array( 'get_comment_under', avatar_add_dict.get_int_array('get_comment_under', 2))) avatar_add.add_child( Node.s32_array('new_hair', avatar_add_dict.get_int_array('new_hair', 2))) avatar_add.add_child( Node.s32_array('new_face', avatar_add_dict.get_int_array('new_face', 2))) avatar_add.add_child( Node.s32_array('new_body', avatar_add_dict.get_int_array('new_body', 2))) avatar_add.add_child( Node.s32_array('new_effect', avatar_add_dict.get_int_array('new_effect', 2))) avatar_add.add_child( Node.s32_array('new_object', avatar_add_dict.get_int_array('new_object', 2))) avatar_add.add_child( Node.s32_array( 'new_comment_over', avatar_add_dict.get_int_array('new_comment_over', 2))) avatar_add.add_child( Node.s32_array( 'new_comment_under', avatar_add_dict.get_int_array('new_comment_under', 2))) # Net VS section netvs = Node.void('netvs') root.add_child(netvs) netvs.add_child(Node.s32('rank_point', 0)) netvs.add_child(Node.s16_array('record', [0, 0, 0, 0, 0, 0])) netvs.add_child(Node.u8('rank', 0)) netvs.add_child(Node.s8('vs_rank_old', 0)) netvs.add_child(Node.s8_array('ojama_condition', [0] * 74)) netvs.add_child(Node.s8_array('set_ojama', [0, 0, 0])) netvs.add_child(Node.s8_array('set_recommend', [0, 0, 0])) netvs.add_child(Node.u8('netvs_play_cnt', 0)) for dialog in [0, 1, 2, 3, 4, 5]: # TODO: Configure this, maybe? netvs.add_child(Node.string('dialog', 'dialog#{}'.format(dialog))) sp_data = Node.void('sp_data') root.add_child(sp_data) sp_data.add_child(Node.s32('sp', profile.get_int('sp', 0))) gakuen = Node.void('gakuen_data') root.add_child(gakuen) gakuen.add_child(Node.s32('music_list', -1)) saucer = Node.void('flying_saucer') root.add_child(saucer) saucer.add_child(Node.s32('music_list', -1)) saucer.add_child(Node.s32('tune_count', -1)) saucer.add_child(Node.u32('clear_norma', 0)) saucer.add_child(Node.u32('clear_norma_add', 0)) zoo_dict = profile.get_dict('zoo') zoo = Node.void('zoo') root.add_child(zoo) zoo.add_child( Node.u16_array('point', zoo_dict.get_int_array('point', 5))) zoo.add_child( Node.s32_array('music_list', zoo_dict.get_int_array('music_list', 2))) zoo.add_child( Node.s8_array('today_play_flag', zoo_dict.get_int_array('today_play_flag', 4))) triple = Node.void('triple_journey') root.add_child(triple) triple.add_child(Node.s32('music_list', -1)) triple.add_child( Node.s32_array('boss_damage', [65534, 65534, 65534, 65534])) triple.add_child(Node.s32_array('boss_stun', [0, 0, 0, 0])) triple.add_child(Node.s32('magic_gauge', 0)) triple.add_child(Node.s32('today_party', 0)) triple.add_child(Node.bool('union_magic', False)) triple.add_child(Node.float('base_attack_rate', 1.0)) triple.add_child(Node.s32('iidx_play_num', 0)) triple.add_child(Node.s32('reflec_play_num', 0)) triple.add_child(Node.s32('voltex_play_num', 0)) triple.add_child(Node.bool('iidx_play_flg', True)) triple.add_child(Node.bool('reflec_play_flg', True)) triple.add_child(Node.bool('voltex_play_flg', True)) ios = Node.void('ios') root.add_child(ios) ios.add_child(Node.s32('continueRightAnswer', 30)) ios.add_child(Node.s32('totalRightAnswer', 30)) kac2013 = Node.void('kac2013') root.add_child(kac2013) kac2013.add_child(Node.s8('music_num', 0)) kac2013.add_child(Node.s16('music', 0)) kac2013.add_child(Node.u8('sheet', 0)) baseball = Node.void('baseball_data') root.add_child(baseball) baseball.add_child(Node.s64('music_list', -1)) for id in [3, 5, 7]: node = Node.void('floor_infection') root.add_child(node) node.add_child(Node.s32('infection_id', id)) node.add_child(Node.s32('music_list', -1)) return root
def handle_playerdata_request(self, request: Node) -> Optional[Node]: method = request.attribute('method') if method == 'expire': return Node.void('playerdata') elif method == 'logout': return Node.void('playerdata') elif method == 'get': modelstring = request.attribute('model') refid = request.child_value('ref_id') root = self.get_profile_by_refid( refid, self.NEW_PROFILE_ONLY if modelstring is None else self.OLD_PROFILE_ONLY, ) if root is None: root = Node.void('playerdata') root.set_attribute('status', str(Status.NO_PROFILE)) return root elif method == 'conversion': refid = request.child_value('ref_id') name = request.child_value('name') chara = request.child_value('chara') root = self.new_profile_by_refid(refid, name, chara) if root is None: root = Node.void('playerdata') root.set_attribute('status', str(Status.NO_PROFILE)) return root elif method == 'new': refid = request.child_value('ref_id') name = request.child_value('name') root = self.new_profile_by_refid(refid, name) if root is None: root = Node.void('playerdata') root.set_attribute('status', str(Status.NO_PROFILE)) return root elif method == 'set': refid = request.attribute('ref_id') root = Node.void('playerdata') root.add_child(Node.s8('pref', -1)) if refid is None: return root userid = self.data.remote.user.from_refid(self.game, self.version, refid) if userid is None: return root oldprofile = self.get_profile(userid) or ValidatedDict() newprofile = self.unformat_profile(userid, request, oldprofile) if newprofile is not None: self.put_profile(userid, newprofile) root.add_child(Node.string('name', newprofile['name'])) return root elif method == 'friend': refid = request.attribute('ref_id') root = Node.void('playerdata') # Look up our own user ID based on the RefID provided. userid = self.data.remote.user.from_refid(self.game, self.version, refid) if userid is None: root.set_attribute('status', str(Status.NO_PROFILE)) return root # Grab the links that we care about. links = self.data.local.user.get_links(self.game, self.version, userid) profiles: Dict[UserID, ValidatedDict] = {} rivals: List[Link] = [] for link in links: if link.type != 'rival': continue other_profile = self.get_profile(link.other_userid) if other_profile is None: continue profiles[link.other_userid] = other_profile rivals.append(link) for rival in links[:2]: rivalid = rival.other_userid rivalprofile = profiles[rivalid] scores = self.data.remote.music.get_scores( self.game, self.version, rivalid) # First, output general profile info. friend = Node.void('friend') root.add_child(friend) # This might be for having non-active or non-confirmed friends, but setting to 0 makes the # ranking numbers disappear and the player icon show a questionmark. friend.add_child(Node.s8('open', 1)) # Set up some sane defaults. friend.add_child( Node.string('name', rivalprofile.get_str('name', 'なし'))) friend.add_child( Node.string('g_pm_id', ID.format_extid( rivalprofile.get_int('extid')))) friend.add_child( Node.s16('chara', rivalprofile.get_int('chara', -1))) # Perform hiscore/medal conversion. hiscore_array = [0] * int( (((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8) clear_medal = [0] * self.GAME_MAX_MUSIC_ID for score in scores: if score.id > self.GAME_MAX_MUSIC_ID: continue # Skip any scores for chart types we don't support if score.chart not in [ self.CHART_TYPE_EASY, self.CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER, self.CHART_TYPE_EX, ]: continue points = score.points clear_medal[score.id] = clear_medal[ score.id] | self.__format_medal_for_score(score) hiscore_index = (score.id * 4) + { self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION, self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION, self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER_POSITION, self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX_POSITION, }[score.chart] hiscore_byte_pos = int((hiscore_index * 17) / 8) hiscore_bit_pos = int((hiscore_index * 17) % 8) hiscore_value = points << hiscore_bit_pos hiscore_array[hiscore_byte_pos] = hiscore_array[ hiscore_byte_pos] | (hiscore_value & 0xFF) hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ( (hiscore_value >> 8) & 0xFF) hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ( (hiscore_value >> 16) & 0xFF) hiscore = bytes(hiscore_array) friend.add_child(Node.u16_array('clear_medal', clear_medal)) friend.add_child(Node.binary('hiscore', hiscore)) return root # Invalid method return None
def handle_player22_request(self, request: Node) -> Optional[Node]: method = request.attribute('method') if method == 'read': refid = request.child_value('ref_id') # Pop'n Music 22 doesn't send a modelstring to load old profiles, # it just expects us to know. So always look for old profiles in # Pop'n 22 land. root = self.get_profile_by_refid(refid, self.OLD_PROFILE_FALLTHROUGH) if root is None: root = Node.void('player22') root.set_attribute('status', str(Status.NO_PROFILE)) return root elif method == 'new': refid = request.child_value('ref_id') name = request.child_value('name') root = self.new_profile_by_refid(refid, name) if root is None: root = Node.void('player22') root.set_attribute('status', str(Status.NO_PROFILE)) return root elif method == 'start': return Node.void('player22') elif method == 'logout': return Node.void('player22') elif method == 'write': refid = request.child_value('ref_id') root = Node.void('player22') if refid is None: return root userid = self.data.remote.user.from_refid(self.game, self.version, refid) if userid is None: return root oldprofile = self.get_profile(userid) or ValidatedDict() newprofile = self.unformat_profile(userid, request, oldprofile) if newprofile is not None: self.put_profile(userid, newprofile) return root elif method == 'friend': refid = request.attribute('ref_id') no = int(request.attribute('no', '-1')) root = Node.void('player22') if no < 0: root.add_child(Node.s8('result', 2)) return root # Look up our own user ID based on the RefID provided. userid = self.data.remote.user.from_refid(self.game, self.version, refid) if userid is None: root.add_child(Node.s8('result', 2)) return root # Grab the links that we care about. links = self.data.local.user.get_links(self.game, self.version, userid) profiles: Dict[UserID, ValidatedDict] = {} rivals: List[Link] = [] for link in links: if link.type != 'rival': continue other_profile = self.get_profile(link.other_userid) if other_profile is None: continue profiles[link.other_userid] = other_profile rivals.append(link) # Somehow requested an invalid profile. if no >= len(rivals): root.add_child(Node.s8('result', 2)) return root rivalid = links[no].other_userid rivalprofile = profiles[rivalid] scores = self.data.remote.music.get_scores(self.game, self.version, rivalid) # First, output general profile info. friend = Node.void('friend') root.add_child(friend) friend.add_child(Node.s16('no', no)) friend.add_child( Node.string('g_pm_id', ID.format_extid(rivalprofile.get_int('extid')))) friend.add_child( Node.string('name', rivalprofile.get_str('name', 'なし'))) friend.add_child( Node.s16('chara', rivalprofile.get_int('chara', -1))) # This might be for having non-active or non-confirmed friends, but setting to 0 makes the # ranking numbers disappear and the player icon show a questionmark. friend.add_child(Node.s8('is_open', 1)) for score in scores: # Skip any scores for chart types we don't support if score.chart not in [ self.CHART_TYPE_EASY, self.CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER, self.CHART_TYPE_EX, ]: continue points = score.points medal = score.data.get_int('medal') music = Node.void('music') friend.add_child(music) music.set_attribute('music_num', str(score.id)) music.set_attribute( 'sheet_num', str({ self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY, self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER, self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX, }[score.chart])) music.set_attribute('score', str(points)) music.set_attribute( 'clearmedal', str({ self.PLAY_MEDAL_CIRCLE_FAILED: self.GAME_PLAY_MEDAL_CIRCLE_FAILED, self.PLAY_MEDAL_DIAMOND_FAILED: self.GAME_PLAY_MEDAL_DIAMOND_FAILED, self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED, self.PLAY_MEDAL_EASY_CLEAR: self.GAME_PLAY_MEDAL_EASY_CLEAR, self.PLAY_MEDAL_CIRCLE_CLEARED: self.GAME_PLAY_MEDAL_CIRCLE_CLEARED, self.PLAY_MEDAL_DIAMOND_CLEARED: self.GAME_PLAY_MEDAL_DIAMOND_CLEARED, self.PLAY_MEDAL_STAR_CLEARED: self.GAME_PLAY_MEDAL_STAR_CLEARED, self.PLAY_MEDAL_CIRCLE_FULL_COMBO: self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO, self.PLAY_MEDAL_DIAMOND_FULL_COMBO: self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO, self.PLAY_MEDAL_STAR_FULL_COMBO: self.GAME_PLAY_MEDAL_STAR_FULL_COMBO, self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT, }[medal])) return root elif method == 'conversion': refid = request.child_value('ref_id') name = request.child_value('name') chara = request.child_value('chara') root = self.new_profile_by_refid(refid, name, chara) if root is None: root = Node.void('playerdata') root.set_attribute('status', str(Status.NO_PROFILE)) return root elif method == 'write_music': refid = request.child_value('ref_id') root = Node.void('player22') if refid is None: return root userid = self.data.remote.user.from_refid(self.game, self.version, refid) if userid is None: return root songid = request.child_value('music_num') chart = { self.GAME_CHART_TYPE_EASY: self.CHART_TYPE_EASY, self.GAME_CHART_TYPE_NORMAL: self.CHART_TYPE_NORMAL, self.GAME_CHART_TYPE_HYPER: self.CHART_TYPE_HYPER, self.GAME_CHART_TYPE_EX: self.CHART_TYPE_EX, }[request.child_value('sheet_num')] medal = request.child_value('clearmedal') points = request.child_value('score') combo = request.child_value('combo') stats = { 'cool': request.child_value('cool'), 'great': request.child_value('great'), 'good': request.child_value('good'), 'bad': request.child_value('bad') } medal = { self.GAME_PLAY_MEDAL_CIRCLE_FAILED: self.PLAY_MEDAL_CIRCLE_FAILED, self.GAME_PLAY_MEDAL_DIAMOND_FAILED: self.PLAY_MEDAL_DIAMOND_FAILED, self.GAME_PLAY_MEDAL_STAR_FAILED: self.PLAY_MEDAL_STAR_FAILED, self.GAME_PLAY_MEDAL_EASY_CLEAR: self.PLAY_MEDAL_EASY_CLEAR, self.GAME_PLAY_MEDAL_CIRCLE_CLEARED: self.PLAY_MEDAL_CIRCLE_CLEARED, self.GAME_PLAY_MEDAL_DIAMOND_CLEARED: self.PLAY_MEDAL_DIAMOND_CLEARED, self.GAME_PLAY_MEDAL_STAR_CLEARED: self.PLAY_MEDAL_STAR_CLEARED, self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO: self.PLAY_MEDAL_CIRCLE_FULL_COMBO, self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO: self.PLAY_MEDAL_DIAMOND_FULL_COMBO, self.GAME_PLAY_MEDAL_STAR_FULL_COMBO: self.PLAY_MEDAL_STAR_FULL_COMBO, self.GAME_PLAY_MEDAL_PERFECT: self.PLAY_MEDAL_PERFECT, }[medal] self.update_score(userid, songid, chart, points, medal, combo=combo, stats=stats) return root # Invalid method return None
def verify_usergamedata_send(self, ref_id: str, ext_id: int, msg_type: str, send_only_common: bool = False) -> None: call = self.call_node() # Set up profile write profiledata = { 'COMMON': [ b'1', b'0', # shoparea spot, filled in below b'3c880f8', b'1', b'0', b'0', b'0', b'0', b'0', b'ffffffffffffffff', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'', # Name spot, filled in below ID.format_extid(ext_id).encode('ascii'), b'', b'', b'', b'', b'', b'', ], 'OPTION': [ b'0', b'3', b'0', b'0', b'0', b'0', b'0', b'3', b'0', b'0', b'0', b'0', b'1', b'2', b'0', b'0', b'0', b'10.000000', b'10.000000', b'10.000000', b'10.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'', b'', b'', b'', b'', b'', b'', b'', ], 'LAST': [ b'1', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'', b'', b'', b'', b'', b'', b'', b'', ], 'RIVAL': [ b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'', b'', b'', b'', b'', b'', b'', b'', ] } if msg_type == 'new': # New profile gets blank name, because we save over it at the end of the round. profiledata['COMMON'][1] = b'0' profiledata['COMMON'][25] = b'' elif msg_type == 'existing': # Exiting profile gets our hardcoded name saved. profiledata['COMMON'][1] = b'3a' profiledata['COMMON'][25] = self.NAME.encode('shift-jis') else: raise Exception(f'Unknown message type {msg_type}!') if send_only_common: profiledata = {'COMMON': profiledata['COMMON']} # Construct node playerdata = Node.void('playerdata') call.add_child(playerdata) playerdata.set_attribute('method', 'usergamedata_send') playerdata.add_child(Node.u32('retrycnt', 0)) info = Node.void('info') playerdata.add_child(info) info.add_child(Node.s32('version', 1)) data = Node.void('data') playerdata.add_child(data) data.add_child(Node.string('refid', ref_id)) data.add_child(Node.string('dataid', ref_id)) data.add_child(Node.string('gamekind', 'MDX')) data.add_child(Node.u32('datanum', len(profiledata.keys()))) record = Node.void('record') data.add_child(record) for ptype in profiledata: profile = [b'ffffffff', ptype.encode('ascii')] + profiledata[ptype] d = Node.string( 'd', base64.b64encode(b','.join(profile)).decode('ascii')) record.add_child(d) d.add_child(Node.string('bin1', '')) # Swap with server resp = self.exchange('', call) self.assert_path(resp, "response/playerdata/result")
def test_format_extid(self) -> None: self.assertEqual(ID.format_extid(12345678), '1234-5678') self.assertEqual(ID.parse_extid('1234-5678'), 12345678) self.assertEqual(ID.parse_extid('bla'), None) self.assertEqual(ID.parse_extid('blah-blah'), None)