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
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
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
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
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 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
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
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
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)