def verify_lobby_entry(self, location: str, extid: int) -> int: call = self.call_node() lobby = Node.void('lobby') lobby.set_attribute('method', 'entry') e = Node.void('e') lobby.add_child(e) e.add_child(Node.s32('eid', 0)) e.add_child(Node.u16('mid', 79)) e.add_child(Node.u8('ng', 0)) e.add_child(Node.s32('uid', extid)) e.add_child(Node.string('pn', self.NAME)) e.add_child(Node.s32('exp', 0)) e.add_child(Node.u8('mg', 0)) e.add_child(Node.s32('tid', 0)) e.add_child(Node.string('tn', '')) e.add_child(Node.string('lid', location)) e.add_child(Node.string('sn', '')) e.add_child(Node.u8('pref', 51)) e.add_child(Node.u8_array('ga', [127, 0, 0, 1])) e.add_child(Node.u16('gp', 10007)) e.add_child(Node.u8_array('la', [16, 0, 0, 0])) call.add_child(lobby) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/lobby/eid") self.assert_path(resp, "response/lobby/e/eid") self.assert_path(resp, "response/lobby/e/mid") self.assert_path(resp, "response/lobby/e/ng") self.assert_path(resp, "response/lobby/e/uid") self.assert_path(resp, "response/lobby/e/pn") self.assert_path(resp, "response/lobby/e/exp") self.assert_path(resp, "response/lobby/e/mg") self.assert_path(resp, "response/lobby/e/tid") self.assert_path(resp, "response/lobby/e/tn") self.assert_path(resp, "response/lobby/e/lid") self.assert_path(resp, "response/lobby/e/sn") self.assert_path(resp, "response/lobby/e/pref") self.assert_path(resp, "response/lobby/e/ga") self.assert_path(resp, "response/lobby/e/gp") self.assert_path(resp, "response/lobby/e/la") return resp.child_value('lobby/eid')
def verify_game_save(self, location: str, refid: str, packet: int, block: int, blaster_energy: int) -> None: call = self.call_node() game = Node.void('game_3') call.add_child(game) game.set_attribute('method', 'save') game.set_attribute('ver', '0') game.add_child(Node.string('refid', refid)) game.add_child(Node.string('locid', location)) game.add_child(Node.u8('headphone', 0)) game.add_child(Node.u16('appeal_id', 1001)) game.add_child(Node.u16('comment_id', 0)) game.add_child(Node.s32('music_id', 29)) game.add_child(Node.u8('music_type', 1)) game.add_child(Node.u8('sort_type', 1)) game.add_child(Node.u8('narrow_down', 0)) game.add_child(Node.u8('gauge_option', 0)) game.add_child(Node.u32('earned_gamecoin_packet', packet)) game.add_child(Node.u32('earned_gamecoin_block', block)) item = Node.void('item') game.add_child(item) info = Node.void('info') item.add_child(info) info.add_child(Node.u32('id', 1)) info.add_child(Node.u32('type', 5)) info.add_child(Node.u32('param', 333333376)) info = Node.void('info') item.add_child(info) info.add_child(Node.u32('id', 0)) info.add_child(Node.u32('type', 5)) info.add_child(Node.u32('param', 600)) game.add_child(Node.s32_array('hidden_param', [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])) game.add_child(Node.s16('skill_name_id', -1)) game.add_child(Node.s32('earned_blaster_energy', blaster_energy)) game.add_child(Node.u32('blaster_count', 0)) printn = Node.void('print') game.add_child(printn) printn.add_child(Node.s32('count', 0)) ea_shop = Node.void('ea_shop') game.add_child(ea_shop) ea_shop.add_child(Node.s32('used_packet_booster', 0)) ea_shop.add_child(Node.s32('used_block_booster', 0)) game.add_child(Node.s8('start_option', 0)) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/game_3")
def verify_game_load(self, cardid: str, refid: str, msg_type: str) -> Dict[str, Any]: call = self.call_node() game = Node.void('game_3') call.add_child(game) game.set_attribute('method', 'load') game.set_attribute('ver', '0') game.add_child(Node.string('dataid', refid)) game.add_child(Node.string('cardid', cardid)) game.add_child(Node.string('refid', refid)) # Swap with server resp = self.exchange('', call) # Verify that response is correct if msg_type == "new": self.assert_path(resp, "response/game_3/result") if resp.child_value('game_3/result') != 1: raise Exception("Invalid result for new profile!") return None if msg_type == "existing": self.assert_path(resp, "response/game_3/name") self.assert_path(resp, "response/game_3/code") self.assert_path(resp, "response/game_3/gamecoin_packet") self.assert_path(resp, "response/game_3/gamecoin_block") self.assert_path(resp, "response/game_3/skill_name_id") self.assert_path(resp, "response/game_3/hidden_param") self.assert_path(resp, "response/game_3/blaster_energy") self.assert_path(resp, "response/game_3/blaster_count") self.assert_path(resp, "response/game_3/play_count") self.assert_path(resp, "response/game_3/daily_count") self.assert_path(resp, "response/game_3/play_chain") self.assert_path(resp, "response/game_3/item") self.assert_path(resp, "response/game_3/skill/course_all") self.assert_path(resp, "response/game_3/story") items: Dict[int, Dict[int, int]] = {} for child in resp.child('game_3/item').children: if child.name != 'info': continue itype = child.child_value('type') iid = child.child_value('id') param = child.child_value('param') if itype not in items: items[itype] = {} items[itype][iid] = param courses: Dict[int, Dict[int, Dict[str, int]]] = {} for child in resp.child('game_3/skill/course_all').children: if child.name != 'd': continue crsid = child.child_value('crsid') season = child.child_value('ssnid') achievement_rate = child.child_value('ar') clear_type = child.child_value('ct') if season not in courses: courses[season] = {} courses[season][crsid] = { 'achievement_rate': achievement_rate, 'clear_type': clear_type, } return { 'name': resp.child_value('game_3/name'), 'packet': resp.child_value('game_3/gamecoin_packet'), 'block': resp.child_value('game_3/gamecoin_block'), 'blaster_energy': resp.child_value('game_3/blaster_energy'), 'items': items, 'courses': courses, } else: raise Exception(f"Invalid game load type {msg_type}")
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: """ Base handler for a profile. Given a userid and a profile dictionary, return a Node representing a profile. Should be overridden. """ return Node.void('game')
def verify_game_load(self, ref_id: str, msg_type: str) -> Dict[str, Any]: call = self.call_node() game = Node.void('game') call.add_child(game) game.set_attribute('method', 'load') game.set_attribute('ver', '2014032400') game.set_attribute('refid', ref_id) # Swap with server resp = self.exchange('', call) if msg_type == 'new': # Verify that response is correct self.assert_path(resp, "response/game/@none") return {} if msg_type == 'existing': # Verify existing profile and return info self.assert_path(resp, "response/game/seq") self.assert_path(resp, "response/game/code") self.assert_path(resp, "response/game/name") self.assert_path(resp, "response/game/area") self.assert_path(resp, "response/game/cnt_s") self.assert_path(resp, "response/game/cnt_d") self.assert_path(resp, "response/game/cnt_b") self.assert_path(resp, "response/game/cnt_m0") self.assert_path(resp, "response/game/cnt_m1") self.assert_path(resp, "response/game/cnt_m2") self.assert_path(resp, "response/game/cnt_m3") self.assert_path(resp, "response/game/cnt_m4") self.assert_path(resp, "response/game/cnt_m5") self.assert_path(resp, "response/game/exp") self.assert_path(resp, "response/game/exp_o") self.assert_path(resp, "response/game/star") self.assert_path(resp, "response/game/star_c") self.assert_path(resp, "response/game/combo") self.assert_path(resp, "response/game/timing_diff") self.assert_path(resp, "response/game/chara") self.assert_path(resp, "response/game/chara_opt") self.assert_path(resp, "response/game/daycount/@playcount") self.assert_path(resp, "response/game/dailycombo/@daily_combo") self.assert_path(resp, "response/game/dailycombo/@daily_combo_lv") self.assert_path(resp, "response/game/last/@cate") self.assert_path(resp, "response/game/last/@cid") self.assert_path(resp, "response/game/last/@ctype") self.assert_path(resp, "response/game/last/@fri") self.assert_path(resp, "response/game/last/@mid") self.assert_path(resp, "response/game/last/@mode") self.assert_path(resp, "response/game/last/@mtype") self.assert_path(resp, "response/game/last/@rival1") self.assert_path(resp, "response/game/last/@rival2") self.assert_path(resp, "response/game/last/@rival3") self.assert_path(resp, "response/game/last/@sid") self.assert_path(resp, "response/game/last/@sort") self.assert_path(resp, "response/game/last/@style") self.assert_path(resp, "response/game/result_star/@slot1") self.assert_path(resp, "response/game/result_star/@slot2") self.assert_path(resp, "response/game/result_star/@slot3") self.assert_path(resp, "response/game/result_star/@slot4") self.assert_path(resp, "response/game/result_star/@slot5") self.assert_path(resp, "response/game/result_star/@slot6") self.assert_path(resp, "response/game/result_star/@slot7") self.assert_path(resp, "response/game/result_star/@slot8") self.assert_path(resp, "response/game/result_star/@slot9") self.assert_path(resp, "response/game/target/@flag") self.assert_path(resp, "response/game/target/@setnum") self.assert_path(resp, "response/game/gr_s/@gr1") self.assert_path(resp, "response/game/gr_s/@gr2") self.assert_path(resp, "response/game/gr_s/@gr3") self.assert_path(resp, "response/game/gr_s/@gr4") self.assert_path(resp, "response/game/gr_s/@gr5") self.assert_path(resp, "response/game/gr_d/@gr1") self.assert_path(resp, "response/game/gr_d/@gr2") self.assert_path(resp, "response/game/gr_d/@gr3") self.assert_path(resp, "response/game/gr_d/@gr4") self.assert_path(resp, "response/game/gr_d/@gr5") self.assert_path(resp, "response/game/opt") self.assert_path(resp, "response/game/opt_ex") self.assert_path(resp, "response/game/flag") self.assert_path(resp, "response/game/rank") for i in range(55): self.assert_path(resp, f"response/game/play_area/@play_cnt{i}") gr_s = resp.child('game/gr_s') gr_d = resp.child('game/gr_d') return { 'name': resp.child_value('game/name'), 'ext_id': resp.child_value('game/code'), 'single_plays': resp.child_value('game/cnt_s'), 'double_plays': resp.child_value('game/cnt_d'), 'groove_single': [ int(gr_s.attribute('gr1')), int(gr_s.attribute('gr2')), int(gr_s.attribute('gr3')), int(gr_s.attribute('gr4')), int(gr_s.attribute('gr5')), ], 'groove_double': [ int(gr_d.attribute('gr1')), int(gr_d.attribute('gr2')), int(gr_d.attribute('gr3')), int(gr_d.attribute('gr4')), int(gr_d.attribute('gr5')), ], } raise Exception('Unknown load type!')
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, f'item_{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_log_opsetting_request(self, request: Node) -> Node: return Node.void('log')
def verify_pcb23_boot(self, loc: str) -> None: call = self.call_node() # Construct node pcb23 = Node.void('pcb23') call.add_child(pcb23) pcb23.set_attribute('method', 'boot') pcb23.add_child(Node.string('loc_id', loc)) pcb23.add_child(Node.u8('loc_type', 0)) pcb23.add_child(Node.string('loc_name', '')) pcb23.add_child(Node.string('country', 'US')) pcb23.add_child(Node.string('region', '.')) pcb23.add_child(Node.s16('pref', 51)) pcb23.add_child(Node.string('customer', '')) pcb23.add_child(Node.string('company', '')) pcb23.add_child(Node.ipv4('gip', '127.0.0.1')) pcb23.add_child(Node.u16('gp', 10011)) pcb23.add_child(Node.string('rom_number', 'M39-JB-G01')) pcb23.add_child(Node.u64('c_drive', 10028228608)) pcb23.add_child(Node.u64('d_drive', 47945170944)) pcb23.add_child(Node.u64('e_drive', 10394677248)) pcb23.add_child(Node.string('etc', '')) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/pcb23/@status")
def verify_player23_read( self, ref_id: str, msg_type: str) -> Dict[str, Dict[int, Dict[str, int]]]: call = self.call_node() # Construct node player23 = Node.void('player23') call.add_child(player23) player23.set_attribute('method', 'read') player23.add_child(Node.string('ref_id', ref_id)) player23.add_child(Node.s8('pref', 51)) # Swap with server resp = self.exchange('', call) if msg_type == 'new': # Verify that response is correct self.assert_path(resp, "response/player23/result") status = resp.child_value('player23/result') if status != 2: raise Exception( f'Reference ID \'{ref_id}\' returned invalid status \'{status}\'' ) return { 'medals': {}, 'items': {}, 'characters': {}, 'lumina': {}, } elif msg_type == 'query': # Verify that the response is correct self.__verify_profile(resp) self.assert_path(resp, "response/player23/result") status = resp.child_value('player23/result') if status != 0: raise Exception( f'Reference ID \'{ref_id}\' returned invalid status \'{status}\'' ) name = resp.child_value('player23/account/name') if name != self.NAME: raise Exception( f'Invalid name \'{name}\' returned for Ref ID \'{ref_id}\'' ) # Medals and items items: Dict[int, Dict[str, int]] = {} medals: Dict[int, Dict[str, int]] = {} charas: Dict[int, Dict[str, int]] = {} for obj in resp.child('player23').children: if obj.name == 'medal': medals[obj.child_value('medal_id')] = { 'level': obj.child_value('level'), 'exp': obj.child_value('exp'), } elif obj.name == 'item': items[obj.child_value('id')] = { 'type': obj.child_value('type'), 'param': obj.child_value('param'), } elif obj.name == 'chara_param': charas[obj.child_value('chara_id')] = { 'friendship': obj.child_value('friendship'), } return { 'medals': medals, 'items': items, 'characters': charas, 'lumina': { 0: { 'lumina': resp.child_value('player23/account/lumina') } }, } else: raise Exception(f'Unrecognized message type \'{msg_type}\'')
def handle_lobby_request(self, request: Node) -> Optional[Node]: # Stub out the entire lobby service return Node.void('lobby')
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.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))) # Number of rivals that are active for this version. links = self.data.local.user.get_links(self.game, self.version, userid) rivalcount = 0 for link in links: if link.type != 'rival': continue if not self.has_profile(link.other_userid): continue # This profile is valid. rivalcount += 1 base.add_child(Node.u8('active_fr_num', rivalcount)) last_played = [x[0] for x in self.data.local.music.get_last_played(self.game, self.music_version, userid, 3)] most_played = [x[0] for x in self.data.local.music.get_most_played(self.game, self.music_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.music_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', f'dialog#{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 handle_game_request(self, request: Node) -> Optional[Node]: method = request.attribute('method') if method == 'get': # TODO: Hook these up to config so we can change this root = Node.void('game') root.add_child(Node.s32('game_phase', 2)) root.add_child(Node.s32('ir_phase', 0)) root.add_child(Node.s32('event_phase', 5)) root.add_child(Node.s32('netvs_phase', 0)) root.add_child(Node.s32('card_phase', 6)) root.add_child(Node.s32('illust_phase', 2)) root.add_child(Node.s32('psp_phase', 5)) root.add_child(Node.s32('other_phase', 1)) root.add_child(Node.s32('jubeat_phase', 1)) root.add_child(Node.s32('public_phase', 3)) root.add_child(Node.s32('kac_phase', 2)) root.add_child(Node.s32('local_matching', 1)) root.add_child(Node.s32('n_matching_sec', 60)) root.add_child(Node.s32('l_matching_sec', 60)) root.add_child(Node.s32('is_check_cpu', 0)) root.add_child(Node.s32('week_no', 0)) root.add_child(Node.s32_array('ng_illust', [0] * 10)) root.add_child(Node.s16_array('sel_ranking', [-1] * 10)) root.add_child(Node.s16_array('up_ranking', [-1] * 10)) return root if method == 'active': # Update the name of this cab for admin purposes self.update_machine_name(request.child_value('shop_name')) return Node.void('game') if method == 'taxphase': return Node.void('game') # Invalid method return None
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.music_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 unformat_profile(self, userid: UserID, request: Node, oldprofile: ValidatedDict) -> ValidatedDict: # For some reason, Pop'n 20 sends us two profile saves, one with 'not done yet' # so we only want to process the done yet node. The 'not gameover' save has # jubeat collabo stuff set in it, but we don't use that so it doesn't matter. if request.child_value('is_not_gameover') == 1: return oldprofile newprofile = copy.deepcopy(oldprofile) newprofile.replace_int('option', request.child_value('option')) newprofile.replace_int('chara', request.child_value('chara')) newprofile.replace_int('mode', request.child_value('mode')) newprofile.replace_int('button', request.child_value('button')) newprofile.replace_int('music', request.child_value('music')) newprofile.replace_int('sheet', request.child_value('sheet')) newprofile.replace_int('last_play_flag', request.child_value('last_play_flag')) newprofile.replace_int('category', request.child_value('category')) newprofile.replace_int('sub_category', request.child_value('sub_category')) newprofile.replace_int('chara_category', request.child_value('chara_category')) newprofile.replace_int('medal_and_friend', request.child_value('medal_and_friend')) newprofile.replace_int('ep', request.child_value('ep')) newprofile.replace_int_array('sp_color_flg', 2, request.child_value('sp_color_flg')) newprofile.replace_int('read_news', request.child_value('read_news')) newprofile.replace_int('consecutive_days_coupon', request.child_value('consecutive_days_coupon')) newprofile.replace_int('tutorial', request.child_value('tutorial')) newprofile.replace_int('music_open_pt', request.child_value('music_open_pt')) newprofile.replace_int('collabo', request.child_value('collabo')) sp_node = request.child('sp_data') if sp_node is not None: newprofile.replace_int('sp', sp_node.child_value('sp')) reflec_node = request.child('reflec_data') if reflec_node is not None: newprofile.replace_int_array('reflec', 2, reflec_node.child_value('reflec')) # Keep track of play statistics self.update_play_statistics(userid) # Extract player card stuff player_card_dict = newprofile.get_dict('player_card') player_card_dict.replace_int_array('title', 2, request.child_value('title')) player_card_dict.replace_int('frame', request.child_value('frame')) player_card_dict.replace_int('base', request.child_value('base')) player_card_dict.replace_int_array('seal', 2, request.child_value('seal')) player_card_dict.replace_int_array('get_title', 4, request.child_value('get_title')) player_card_dict.replace_int('get_frame', request.child_value('get_frame')) player_card_dict.replace_int('get_base', request.child_value('get_base')) player_card_dict.replace_int_array('get_seal', 2, request.child_value('get_seal')) player_card_ex = request.child('player_card_ex') if player_card_ex is not None: player_card_dict.replace_int('get_title_ex', player_card_ex.child_value('get_title_ex')) player_card_dict.replace_int('get_frame_ex', player_card_ex.child_value('get_frame_ex')) player_card_dict.replace_int('get_base_ex', player_card_ex.child_value('get_base_ex')) player_card_dict.replace_int('get_seal_ex', player_card_ex.child_value('get_seal_ex')) newprofile.replace_dict('player_card', player_card_dict) # Extract navigate stuff navigate_dict = newprofile.get_dict('navigate') navigate = request.child('navigate') if navigate is not None: navigate_dict.replace_int('genre', navigate.child_value('genre')) navigate_dict.replace_int('image', navigate.child_value('image')) navigate_dict.replace_int('level', navigate.child_value('level')) navigate_dict.replace_int('ojama', navigate.child_value('ojama')) navigate_dict.replace_int('limit_num', navigate.child_value('limit_num')) navigate_dict.replace_int('button', navigate.child_value('button')) navigate_dict.replace_int('life', navigate.child_value('life')) navigate_dict.replace_int('progress', navigate.child_value('progress')) newprofile.replace_dict('navigate', navigate_dict) # Extract scores for node in request.children: if node.name == 'stage': songid = node.child_value('no') chart = { self.GAME_CHART_TYPE_EASY: self.CHART_TYPE_EASY, self.GAME_CHART_TYPE_NORMAL: self.CHART_TYPE_NORMAL, self.GAME_CHART_TYPE_HYPER: self.CHART_TYPE_HYPER, self.GAME_CHART_TYPE_EX: self.CHART_TYPE_EX, }[node.child_value('sheet')] medal = (node.child_value('n_data') >> (chart * 4)) & 0x000F medal = { self.GAME_PLAY_MEDAL_CIRCLE_FAILED: self.PLAY_MEDAL_CIRCLE_FAILED, self.GAME_PLAY_MEDAL_DIAMOND_FAILED: self.PLAY_MEDAL_DIAMOND_FAILED, self.GAME_PLAY_MEDAL_STAR_FAILED: self.PLAY_MEDAL_STAR_FAILED, self.GAME_PLAY_MEDAL_CIRCLE_CLEARED: self.PLAY_MEDAL_CIRCLE_CLEARED, self.GAME_PLAY_MEDAL_DIAMOND_CLEARED: self.PLAY_MEDAL_DIAMOND_CLEARED, self.GAME_PLAY_MEDAL_STAR_CLEARED: self.PLAY_MEDAL_STAR_CLEARED, self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO: self.PLAY_MEDAL_CIRCLE_FULL_COMBO, self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO: self.PLAY_MEDAL_DIAMOND_FULL_COMBO, self.GAME_PLAY_MEDAL_STAR_FULL_COMBO: self.PLAY_MEDAL_STAR_FULL_COMBO, self.GAME_PLAY_MEDAL_PERFECT: self.PLAY_MEDAL_PERFECT, }[medal] points = node.child_value('score') self.update_score(userid, songid, chart, points, medal) return newprofile
def handle_player_read_request(self, request: Node) -> Node: refid = request.child_value('rid') profile = self.get_profile_by_refid(refid) if profile: return profile return Node.void('player')
def verify_player23_write( self, ref_id: str, medal: Optional[Dict[str, int]] = None, item: Optional[Dict[str, int]] = None, character: Optional[Dict[str, int]] = None, ) -> None: call = self.call_node() # Construct node player23 = Node.void('player23') call.add_child(player23) player23.set_attribute('method', 'write') player23.add_child(Node.string('ref_id', ref_id)) # Add required children config = Node.void('config') player23.add_child(config) config.add_child(Node.s16('chara', 1543)) if medal is not None: medalnode = Node.void('medal') player23.add_child(medalnode) medalnode.add_child(Node.s16('medal_id', medal['id'])) medalnode.add_child(Node.s16('level', medal['level'])) medalnode.add_child(Node.s32('exp', medal['exp'])) medalnode.add_child(Node.s32('set_count', 0)) medalnode.add_child(Node.s32('get_count', 0)) if item is not None: itemnode = Node.void('item') player23.add_child(itemnode) itemnode.add_child(Node.u8('type', item['type'])) itemnode.add_child(Node.u16('id', item['id'])) itemnode.add_child(Node.u16('param', item['param'])) itemnode.add_child(Node.bool('is_new', False)) if character is not None: chara_param = Node.void('chara_param') player23.add_child(chara_param) chara_param.add_child(Node.u16('chara_id', character['id'])) chara_param.add_child( Node.u16('friendship', character['friendship'])) # Swap with server resp = self.exchange('', call) self.assert_path(resp, "response/player23/@status")
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: statistics = self.get_play_statistics(userid) game_config = self.get_game_config() achievements = self.data.local.user.get_achievements(self.game, self.version, userid) scores = self.data.remote.music.get_scores(self.game, self.version, userid) root = Node.void('player') pdata = Node.void('pdata') root.add_child(pdata) base = Node.void('base') pdata.add_child(base) base.add_child(Node.s32('uid', profile.get_int('extid'))) base.add_child(Node.string('name', profile.get_str('name'))) base.add_child(Node.s16('lv', profile.get_int('lvl'))) base.add_child(Node.s32('exp', profile.get_int('exp'))) base.add_child(Node.s16('mg', profile.get_int('mg'))) base.add_child(Node.s16('ap', profile.get_int('ap'))) base.add_child(Node.s32('flag', profile.get_int('flag'))) 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 con = Node.void('con') pdata.add_child(con) con.add_child(Node.s32('day', today_count)) con.add_child(Node.s32('cnt', statistics.get_int('total_plays'))) con.add_child(Node.s32('last', statistics.get_int('last_play_timestamp'))) con.add_child(Node.s32('now', Time.now())) team = Node.void('team') pdata.add_child(team) team.add_child(Node.s32('id', -1)) team.add_child(Node.string('name', '')) custom = Node.void('custom') customdict = profile.get_dict('custom') pdata.add_child(custom) custom.add_child(Node.u8('bgm_m', customdict.get_int('bgm_m'))) custom.add_child(Node.u8('st_f', customdict.get_int('st_f'))) custom.add_child(Node.u8('st_bg', customdict.get_int('st_bg'))) custom.add_child(Node.u8('st_bg_b', customdict.get_int('st_bg_b'))) custom.add_child(Node.u8('eff_e', customdict.get_int('eff_e'))) custom.add_child(Node.u8('se_s', customdict.get_int('se_s'))) custom.add_child(Node.u8('se_s_v', customdict.get_int('se_s_v'))) released = Node.void('released') pdata.add_child(released) 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 == 0: # Don't echo unlocks when we're force unlocking, we'll do it later continue info = Node.void('info') released.add_child(info) info.add_child(Node.u8('type', itemtype)) info.add_child(Node.u16('id', item.id)) if game_config.get_bool('force_unlock_songs'): songs = {song.id for song in self.data.local.music.get_all_songs(self.game, self.version)} for songid in songs: info = Node.void('info') released.add_child(info) info.add_child(Node.u8('type', 0)) info.add_child(Node.u16('id', songid)) # Scores record = Node.void('record') pdata.add_child(record) for score in scores: rec = Node.void('rec') record.add_child(rec) rec.add_child(Node.u16('mid', score.id)) rec.add_child(Node.u8('ng', score.chart)) rec.add_child(Node.s32('win', score.data.get_dict('stats').get_int('win'))) rec.add_child(Node.s32('lose', score.data.get_dict('stats').get_int('lose'))) rec.add_child(Node.s32('draw', score.data.get_dict('stats').get_int('draw'))) rec.add_child(Node.u8('ct', self.__db_to_game_clear_type(score.data.get_int('clear_type'), score.data.get_int('combo_type')))) rec.add_child(Node.s16('ar', int(score.data.get_int('achievement_rate') / 10))) rec.add_child(Node.s16('bs', score.points)) rec.add_child(Node.s16('mc', score.data.get_int('combo'))) rec.add_child(Node.s16('bmc', score.data.get_int('miss_count'))) # In original ReflecBeat, the entire battle log was returned for each battle. # We don't support storing all of that info, so don't return anything here. blog = Node.void('blog') pdata.add_child(blog) # Comment (seems unused?) pdata.add_child(Node.string('cmnt', '')) return root
def verify_player23_write_music(self, ref_id: str, score: Dict[str, Any]) -> None: call = self.call_node() # Construct node player23 = Node.void('player23') call.add_child(player23) player23.set_attribute('method', 'write_music') player23.add_child(Node.string('ref_id', ref_id)) player23.add_child(Node.string('data_id', ref_id)) player23.add_child(Node.string('name', self.NAME)) player23.add_child(Node.u8('stage', 0)) player23.add_child(Node.s16('music_num', score['id'])) player23.add_child(Node.u8('sheet_num', score['chart'])) player23.add_child(Node.u8('clearmedal', score['medal'])) player23.add_child(Node.s32('score', score['score'])) player23.add_child(Node.s16('combo', 0)) player23.add_child(Node.s16('cool', 0)) player23.add_child(Node.s16('great', 0)) player23.add_child(Node.s16('good', 0)) player23.add_child(Node.s16('bad', 0)) # Swap with server resp = self.exchange('', call) self.assert_path(resp, "response/player23/@status")
def handle_log_pcb_status_request(self, request: Node) -> Node: return Node.void('log')
def handle_sysinfo_fan_request(self, request: Node) -> Node: sysinfo = Node.void('sysinfo') sysinfo.add_child(Node.u8('pref', 51)) sysinfo.add_child(Node.string('lid', request.child_value('lid'))) return sysinfo
def handle_log_play_request(self, request: Node) -> Node: return Node.void('log')
def handle_lobby_entry_request(self, request: Node) -> Node: root = Node.void('lobby') # Create a lobby entry for this user extid = request.child_value('e/uid') userid = self.data.remote.user.from_extid(self.game, self.version, extid) if userid is not None: profile = self.get_profile(userid) self.data.local.lobby.put_lobby( self.game, self.version, userid, { 'mid': request.child_value('e/mid'), 'ng': request.child_value('e/ng'), 'lid': request.child_value('e/lid'), 'sn': request.child_value('e/sn'), 'pref': request.child_value('e/pref'), 'ga': request.child_value('e/ga'), 'gp': request.child_value('e/gp'), 'la': request.child_value('e/la'), } ) lobby = self.data.local.lobby.get_lobby( self.game, self.version, userid, ) root.add_child(Node.s32('eid', lobby.get_int('id'))) e = Node.void('e') root.add_child(e) e.add_child(Node.s32('eid', lobby.get_int('id'))) e.add_child(Node.u16('mid', lobby.get_int('mid'))) e.add_child(Node.u8('ng', lobby.get_int('ng'))) e.add_child(Node.s32('uid', profile.get_int('extid'))) e.add_child(Node.string('pn', profile.get_str('name'))) e.add_child(Node.s32('exp', profile.get_int('exp'))) e.add_child(Node.u8('mg', profile.get_int('mg'))) e.add_child(Node.s32('tid', lobby.get_int('tid'))) e.add_child(Node.string('tn', lobby.get_str('tn'))) e.add_child(Node.string('lid', lobby.get_str('lid'))) e.add_child(Node.string('sn', lobby.get_str('sn'))) e.add_child(Node.u8('pref', lobby.get_int('pref'))) e.add_child(Node.u8_array('ga', lobby.get_int_array('ga', 4))) e.add_child(Node.u16('gp', lobby.get_int('gp'))) e.add_child(Node.u8_array('la', lobby.get_int_array('la', 4))) return root
def handle(self, tree: Node) -> Optional[Node]: """ Given a packet from a game, handle it and return a response. Parameters: tree - A Node representing the root of a tree. Expected to come from an external game. Returns: A Node representing the root of a response tree, or None if we had a problem parsing or generating a response. """ self.log("Received request:\n{}", tree) if tree.name != 'call': # Invalid request self.log("Invalid root node {}", tree.name) return None if len(tree.children) != 1: # Invalid request self.log("Invalid number of children for root node") return None modelstring = tree.attribute('model') model = Model.from_modelstring(modelstring) pcbid = tree.attribute('srcid') # If we are enforcing, bail out if we don't recognize thie ID pcb = self.__data.local.machine.get_machine(pcbid) if self.__config['server']['enforce_pcbid'] and pcb is None: self.log("Unrecognized PCBID {}", pcbid) raise UnrecognizedPCBIDException( pcbid, modelstring, self.__config['client']['address']) # If we don't have a Machine, but we aren't enforcing, we must create it if pcb is None: pcb = self.__data.local.machine.create_machine(pcbid) request = tree.children[0] config = copy.copy(self.__config) config['machine'] = { 'pcbid': pcbid, 'arcade': pcb.arcade, } # If the machine we looked up is in an arcade, override the global # paseli settings with the arcade paseli settings. if pcb.arcade is not None: arcade = self.__data.local.machine.get_arcade(pcb.arcade) if arcade is not None: config['paseli']['enabled'] = arcade.data.get_bool( 'paseli_enabled') config['paseli']['infinite'] = arcade.data.get_bool( 'paseli_infinite') if arcade.data.get_bool('mask_services_url'): # Mask the address, no matter what the server settings are config['server']['uri'] = None # If we don't have a server URI, we should add the default if 'uri' not in config['server']: config['server']['uri'] = None game = Base.create(self.__data, config, model) method = request.attribute('method') response = None # If we are enforcing, make sure the PCBID isn't specified to be # game-specific if self.__config['server']['enforce_pcbid'] and pcb.game is not None: if pcb.game != game.game: self.log( "PCBID {} assigned to game {}, but connected from game {}", pcbid, pcb.game, game.game) raise UnrecognizedPCBIDException( pcbid, modelstring, self.__config['client']['address']) if pcb.version is not None: if pcb.version > 0 and pcb.version != game.version: self.log( "PCBID {} assigned to game {} version {}, but connected from game {} version {}", pcbid, pcb.game, pcb.version, game.game, game.version, ) raise UnrecognizedPCBIDException( pcbid, modelstring, self.__config['client']['address']) if pcb.version < 0 and (-pcb.version) < game.version: self.log( "PCBID {} assigned to game {} maximum version {}, but connected from game {} version {}", pcbid, pcb.game, -pcb.version, game.game, game.version, ) raise UnrecognizedPCBIDException( pcbid, modelstring, self.__config['client']['address']) # First, try to handle with specific service/method function try: handler = getattr( game, 'handle_{}_{}_request'.format(request.name, method)) except AttributeError: handler = None if handler is not None: response = handler(request) if response is None: # Now, try to pass it off to a generic service handler try: handler = getattr(game, 'handle_{}_request'.format(request.name)) except AttributeError: handler = None if handler is not None: response = handler(request) if response is None: # Unrecognized handler self.log("Unrecognized service {} method {}".format( request.name, method)) return None # Make sure we have a status value if one wasn't provided if 'status' not in response.attributes: response.set_attribute('status', str(Status.SUCCESS)) root = Node.void('response') root.add_child(response) root.set_attribute('dstid', pcbid) self.log("Sending response:\n{}", root) return root
def handle_lobby_read_request(self, request: Node) -> Node: root = Node.void('lobby') # Look up all lobbies matching the criteria specified mg = request.child_value('m_grade') # noqa: F841 extid = request.child_value('uid') limit = request.child_value('max') userid = self.data.remote.user.from_extid(self.game, self.version, extid) if userid is not None: lobbies = self.data.local.lobby.get_all_lobbies(self.game, self.version) for (user, lobby) in lobbies: if limit <= 0: break if user == userid: # If we have our own lobby, don't return it continue profile = self.get_profile(user) if profile is None: # No profile info, don't return this lobby continue e = Node.void('e') root.add_child(e) e.add_child(Node.s32('eid', lobby.get_int('id'))) e.add_child(Node.u16('mid', lobby.get_int('mid'))) e.add_child(Node.u8('ng', lobby.get_int('ng'))) e.add_child(Node.s32('uid', profile.get_int('extid'))) e.add_child(Node.string('pn', profile.get_str('name'))) e.add_child(Node.s32('exp', profile.get_int('exp'))) e.add_child(Node.u8('mg', profile.get_int('mg'))) e.add_child(Node.s32('tid', lobby.get_int('tid'))) e.add_child(Node.string('tn', lobby.get_str('tn'))) e.add_child(Node.string('lid', lobby.get_str('lid'))) e.add_child(Node.string('sn', lobby.get_str('sn'))) e.add_child(Node.u8('pref', lobby.get_int('pref'))) e.add_child(Node.u8_array('ga', lobby.get_int_array('ga', 4))) e.add_child(Node.u16('gp', lobby.get_int('gp'))) e.add_child(Node.u8_array('la', lobby.get_int_array('la', 4))) limit = limit - 1 return root
def verify_eventlog_write(self, location: str) -> None: call = self.call_node() # Construct node eventlog = Node.void('eventlog') call.add_child(eventlog) eventlog.set_attribute('method', 'write') eventlog.add_child(Node.u32('retrycnt', 0)) data = Node.void('data') eventlog.add_child(data) data.add_child(Node.string('eventid', 'S_PWRON')) data.add_child(Node.s32('eventorder', 0)) data.add_child(Node.u64('pcbtime', int(time.time() * 1000))) data.add_child(Node.s64('gamesession', -1)) data.add_child(Node.string('strdata1', '2.3.8')) data.add_child(Node.string('strdata2', '')) data.add_child(Node.s64('numdata1', 1)) data.add_child(Node.s64('numdata2', 0)) data.add_child(Node.string('locationid', location)) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/eventlog/gamesession") self.assert_path(resp, "response/eventlog/logsendflg") self.assert_path(resp, "response/eventlog/logerrlevel") self.assert_path(resp, "response/eventlog/evtidnosendflg")
def handle_lobby_delete_request(self, request: Node) -> Node: eid = request.child_value('eid') self.data.local.lobby.destroy_lobby(eid) return Node.void('lobby')
def verify_game_buy(self, refid: str, catalogtype: int, catalogid: int, currencytype: int, price: int, itemtype: int, itemid: int, param: int, success: bool) -> None: call = self.call_node() game = Node.void('game_3') call.add_child(game) game.set_attribute('ver', '0') game.set_attribute('method', 'buy') game.add_child(Node.string('refid', refid)) game.add_child(Node.u8('catalog_type', catalogtype)) game.add_child(Node.u32('catalog_id', catalogid)) game.add_child(Node.u32('earned_gamecoin_packet', 0)) game.add_child(Node.u32('earned_gamecoin_block', 0)) game.add_child(Node.u32('currency_type', currencytype)) item = Node.void('item') game.add_child(item) item.add_child(Node.u32('item_type', itemtype)) item.add_child(Node.u32('item_id', itemid)) item.add_child(Node.u32('param', param)) item.add_child(Node.u32('price', price)) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/game_3/gamecoin_packet") self.assert_path(resp, "response/game_3/gamecoin_block") self.assert_path(resp, "response/game_3/result") if success: if resp.child_value('game_3/result') != 0: raise Exception('Failed to purchase!') else: if resp.child_value('game_3/result') == 0: raise Exception('Purchased when shouldn\'t have!')
def handle_player_delete_request(self, request: Node) -> Node: return Node.void('player')
def verify_game_entry_s(self) -> int: call = self.call_node() game = Node.void('game_3') call.add_child(game) game.set_attribute('ver', '0') game.set_attribute('method', 'entry_s') game.add_child(Node.u8('c_ver', 101)) game.add_child(Node.u8('p_num', 1)) game.add_child(Node.u8('p_rest', 1)) game.add_child(Node.u8('filter', 1)) game.add_child(Node.u32('mid', 416)) game.add_child(Node.u32('sec', 45)) game.add_child(Node.u16('port', 10007)) game.add_child(Node.fouru8('gip', [127, 0, 0, 1])) game.add_child(Node.fouru8('lip', [10, 0, 5, 73])) game.add_child(Node.u8('claim', 0)) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/game_3/entry_id") return resp.child_value('game_3/entry_id')
def verify_log_play(self, extid: int, loc: str, scores: List[Dict[str, int]]) -> None: call = self.call_node() log = Node.void('log') call.add_child(log) log.set_attribute('method', 'play') log.add_child(Node.s32('uid', extid)) log.add_child(Node.string('lid', loc)) play = Node.void('play') log.add_child(play) play.add_child(Node.s16('stage', len(scores))) play.add_child(Node.s32('sec', 700)) scoreid = 0 for score in scores: rec = Node.void('rec') log.add_child(rec) rec.add_child(Node.s16('idx', scoreid)) rec.add_child(Node.s16('mid', score['id'])) rec.add_child(Node.s16('grade', score['chart'])) rec.add_child(Node.s16('color', 0)) rec.add_child(Node.s16('match', 0)) rec.add_child(Node.s16('res', 0)) rec.add_child(Node.s16('score', score['score'])) rec.add_child(Node.s16('mc', score['combo'])) rec.add_child(Node.s16('jt_jr', 0)) rec.add_child(Node.s16('jt_ju', 0)) rec.add_child(Node.s16('jt_gr', 0)) rec.add_child(Node.s16('jt_gd', 0)) rec.add_child(Node.s16('jt_ms', score['miss_count'])) rec.add_child(Node.s32('sec', 200)) scoreid = scoreid + 1 # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/log/@status")