diff --git a/bemani/backend/popn/sunnypark.py b/bemani/backend/popn/sunnypark.py index 6c474d8..5ceede5 100644 --- a/bemani/backend/popn/sunnypark.py +++ b/bemani/backend/popn/sunnypark.py @@ -1,6 +1,6 @@ # vim: set fileencoding=utf-8 import copy -from typing import Dict, List, Optional +from typing import Dict, List from bemani.backend.popn.base import PopnMusicBase from bemani.backend.popn.fantasia import PopnMusicFantasia @@ -43,7 +43,7 @@ class PopnMusicSunnyPark(PopnMusicBase): # Maximum music ID for this game GAME_MAX_MUSIC_ID = 1350 - def previous_version(self) -> Optional[PopnMusicBase]: + def previous_version(self) -> PopnMusicBase: return PopnMusicFantasia(self.data, self.config, self.model) def __format_medal_for_score(self, score: Score) -> int: @@ -219,7 +219,6 @@ class PopnMusicSunnyPark(PopnMusicBase): 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', f'dialog#{dialog}')) sp_data = Node.void('sp_data') @@ -416,191 +415,179 @@ class PopnMusicSunnyPark(PopnMusicBase): return newprofile - def handle_game_request(self, request: Node) -> Optional[Node]: - method = request.attribute('method') + def handle_game_get_request(self, request: Node) -> Node: + # TODO: Hook these up to config so we can change this + root = Node.void('game') + root.add_child(Node.s32('ir_phase', 0)) + root.add_child(Node.s32('music_open_phase', 8)) + root.add_child(Node.s32('collabo_phase', 8)) + root.add_child(Node.s32('personal_event_phase', 10)) + root.add_child(Node.s32('shop_event_phase', 6)) + root.add_child(Node.s32('netvs_phase', 0)) + root.add_child(Node.s32('card_phase', 9)) + root.add_child(Node.s32('other_phase', 9)) + root.add_child(Node.s32('local_matching_enable', 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.s16_array('sel_ranking', [-1, -1, -1, -1, -1])) + root.add_child(Node.s16_array('up_ranking', [-1, -1, -1, -1, -1])) + return root - if method == 'get': - # TODO: Hook these up to config so we can change this - root = Node.void('game') - root.add_child(Node.s32('ir_phase', 0)) - root.add_child(Node.s32('music_open_phase', 8)) - root.add_child(Node.s32('collabo_phase', 8)) - root.add_child(Node.s32('personal_event_phase', 10)) - root.add_child(Node.s32('shop_event_phase', 6)) - root.add_child(Node.s32('netvs_phase', 0)) - root.add_child(Node.s32('card_phase', 9)) - root.add_child(Node.s32('other_phase', 9)) - root.add_child(Node.s32('local_matching_enable', 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.s16_array('sel_ranking', [-1, -1, -1, -1, -1])) - root.add_child(Node.s16_array('up_ranking', [-1, -1, -1, -1, -1])) - return root + def handle_game_active_request(self, request: Node) -> Node: + # Update the name of this cab for admin purposes + self.update_machine_name(request.child_value('shop_name')) + return Node.void('game') - 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') + def handle_game_taxphase_request(self, request: Node) -> Node: + return Node.void('game') - if method == 'taxphase': - return Node.void('game') + def handle_playerdata_expire_request(self, request: Node) -> Node: + return Node.void('playerdata') - # 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') + def handle_playerdata_logout_request(self, request: Node) -> Node: + return Node.void('playerdata') + def handle_playerdata_get_request(self, request: Node) -> Node: + 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.add_child(Node.s8('pref', -1)) - if refid is None: - return root + root.set_attribute('status', str(Status.NO_PROFILE)) + return root - userid = self.data.remote.user.from_refid(self.game, self.version, refid) - if userid is None: - return root + def handle_playerdata_conversion_request(self, request: Node) -> Node: + 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 - oldprofile = self.get_profile(userid) or Profile(self.game, self.version, refid, 0) - newprofile = self.unformat_profile(userid, request, oldprofile) + def handle_playerdata_new_request(self, request: Node) -> Node: + 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 - if newprofile is not None: - self.put_profile(userid, newprofile) - root.add_child(Node.string('name', newprofile['name'])) + def handle_playerdata_set_request(self, request: Node) -> Node: + refid = request.attribute('ref_id') + root = Node.void('playerdata') + root.add_child(Node.s8('pref', -1)) + if refid is None: return root - elif method == 'friend': - refid = request.attribute('ref_id') - root = Node.void('playerdata') + userid = self.data.remote.user.from_refid(self.game, self.version, refid) + if userid is None: + 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.set_attribute('status', str(Status.NO_PROFILE)) - return root + oldprofile = self.get_profile(userid) or Profile(self.game, self.version, refid, 0) + newprofile = self.unformat_profile(userid, request, oldprofile) - # Grab the links that we care about. - links = self.data.local.user.get_links(self.game, self.version, userid) - profiles: Dict[UserID, Profile] = {} - rivals: List[Link] = [] - for link in links: - if link.type != 'rival': + if newprofile is not None: + self.put_profile(userid, newprofile) + root.add_child(Node.string('name', newprofile['name'])) + + return root + + def handle_playerdata_friend_request(self, request: Node) -> Node: + 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, Profile] = {} + 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.extid))) + friend.add_child(Node.s16('chara', rivalprofile.get_int('chara', -1))) + + # Set up player avatar. + avatar_dict = rivalprofile.get_dict('avatar') + friend.add_child(Node.u8('hair', avatar_dict.get_int('hair', 0))) + friend.add_child(Node.u8('face', avatar_dict.get_int('face', 0))) + friend.add_child(Node.u8('body', avatar_dict.get_int('body', 0))) + friend.add_child(Node.u8('effect', avatar_dict.get_int('effect', 0))) + friend.add_child(Node.u8('object', avatar_dict.get_int('object', 0))) + friend.add_child(Node.u8_array('comment', avatar_dict.get_int_array('comment', 2))) + + # 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 - other_profile = self.get_profile(link.other_userid) - if other_profile is None: + # 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 - 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) + 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 = score.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) - # First, output general profile info. - friend = Node.void('friend') - root.add_child(friend) + hiscore = bytes(hiscore_array) + friend.add_child(Node.u16_array('clear_medal', clear_medal)) + friend.add_child(Node.binary('hiscore', hiscore)) - # 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)) + return root - # 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.extid))) - friend.add_child(Node.s16('chara', rivalprofile.get_int('chara', -1))) - - # Set up player avatar. - avatar_dict = rivalprofile.get_dict('avatar') - friend.add_child(Node.u8('hair', avatar_dict.get_int('hair', 0))) - friend.add_child(Node.u8('face', avatar_dict.get_int('face', 0))) - friend.add_child(Node.u8('body', avatar_dict.get_int('body', 0))) - friend.add_child(Node.u8('effect', avatar_dict.get_int('effect', 0))) - friend.add_child(Node.u8('object', avatar_dict.get_int('object', 0))) - friend.add_child(Node.u8_array('comment', avatar_dict.get_int_array('comment', 2))) - - # 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 - - 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 = score.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_lobby_request(self, request: Node) -> Optional[Node]: + def handle_lobby_request(self, request: Node) -> Node: # Stub out the entire lobby service return Node.void('lobby')