1
0
mirror of synced 2025-01-18 22:24:04 +01:00

Typing fixes for Pop'n 22-24, event ID limits checked and commented, converted Lapistoria to modern routing.

This commit is contained in:
Jennifer Taylor 2021-09-03 22:42:09 +00:00
parent 9d4eccf66e
commit ab4c82b886
4 changed files with 243 additions and 262 deletions

View File

@ -1,7 +1,7 @@
# vim: set fileencoding=utf-8 # vim: set fileencoding=utf-8
import binascii import binascii
import copy import copy
from typing import Dict, List, Optional from typing import Dict, List
from bemani.backend.popn.base import PopnMusicBase from bemani.backend.popn.base import PopnMusicBase
from bemani.backend.popn.lapistoria import PopnMusicLapistoria from bemani.backend.popn.lapistoria import PopnMusicLapistoria
@ -38,7 +38,7 @@ class PopnMusicEclale(PopnMusicBase):
# Biggest ID in the music DB # Biggest ID in the music DB
GAME_MAX_MUSIC_ID = 1550 GAME_MAX_MUSIC_ID = 1550
def previous_version(self) -> Optional[PopnMusicBase]: def previous_version(self) -> PopnMusicBase:
return PopnMusicLapistoria(self.data, self.config, self.model) return PopnMusicLapistoria(self.data, self.config, self.model)
def __construct_common_info(self, root: Node) -> None: def __construct_common_info(self, root: Node) -> None:
@ -69,7 +69,7 @@ class PopnMusicEclale(PopnMusicBase):
10: 4, 10: 4,
# Unknown event (0-1) # Unknown event (0-1)
11: 1, 11: 1,
# Possibly global event matching related? (0-1) # Possibly net taisen related? (0-1)
12: 1, 12: 1,
# Unknown event (0-4) # Unknown event (0-4)
13: 4, 13: 4,
@ -162,7 +162,7 @@ class PopnMusicEclale(PopnMusicBase):
self.__construct_common_info(info) self.__construct_common_info(info)
return info return info
def handle_lobby22_request(self, request: Node) -> Optional[Node]: def handle_lobby22_request(self, request: Node) -> Node:
# Stub out the entire lobby22 service (yes, its lobby22 in Pop'n 23) # Stub out the entire lobby22 service (yes, its lobby22 in Pop'n 23)
return Node.void('lobby22') return Node.void('lobby22')

View File

@ -1,6 +1,6 @@
# vim: set fileencoding=utf-8 # vim: set fileencoding=utf-8
import copy import copy
from typing import Dict, List, Optional from typing import Dict, List
from bemani.backend.popn.base import PopnMusicBase from bemani.backend.popn.base import PopnMusicBase
from bemani.backend.popn.sunnypark import PopnMusicSunnyPark from bemani.backend.popn.sunnypark import PopnMusicSunnyPark
@ -38,286 +38,269 @@ class PopnMusicLapistoria(PopnMusicBase):
# Max valud music ID for conversions and stuff # Max valud music ID for conversions and stuff
GAME_MAX_MUSIC_ID = 1422 GAME_MAX_MUSIC_ID = 1422
def previous_version(self) -> Optional[PopnMusicBase]: def previous_version(self) -> PopnMusicBase:
return PopnMusicSunnyPark(self.data, self.config, self.model) return PopnMusicSunnyPark(self.data, self.config, self.model)
def handle_info22_request(self, request: Node) -> Optional[Node]: def handle_info22_common_request(self, request: Node) -> Node:
method = request.attribute('method') # TODO: Hook these up to config so we can change this
phases = {
# Unknown event (0-16)
0: 0,
# Unknown event (0-11)
1: 0,
# Pop'n Aura, max (0-11) (remove all aura requirements)
2: 11,
# Story (0-24)
3: 1,
# BEMANI ruins Discovery! (0-2)
4: 0,
# Unknown event, something to do with net taisen (0-2)
5: 0,
# Unknown event (0-1)
6: 0,
# Unknown event (0-1)
7: 0,
# Unknown event (0-1)
8: 0,
# Unknown event (0-11)
9: 0,
# Unknown event (0-2)
10: 0,
# Unknown event (0-3)
11: 0,
# Unknown event (0-1)
12: 0,
# Unknown event (0-2)
13: 0,
# Unknown event (0-4)
14: 0,
# Unknown event (0-2)
15: 0,
# Unknown event (0-2)
16: 0,
# Unknown event (0-12)
17: 0,
# Unknown event (0-2)
18: 0,
# Unknown event (0-7)
19: 0,
}
if method == 'common': root = Node.void('info22')
# TODO: Hook these up to config so we can change this for phaseid in phases:
phases = { phase = Node.void('phase')
# Unknown event root.add_child(phase)
0: 0, phase.add_child(Node.s16('event_id', phaseid))
# Unknown event phase.add_child(Node.s16('phase', phases[phaseid]))
1: 0,
# Pop'n Aura, max 10 (remove all aura requirements)
2: 10,
# Story
3: 1,
# BEMANI ruins Discovery!
4: 0,
# Unknown event
5: 0,
# Unknown event
6: 0,
# Unknown event
7: 0,
# Unknown event
8: 0,
# Unknown event
9: 0,
# Unknown event
10: 0,
# Unknown event
11: 0,
# Unknown event
12: 0,
# Unknown event
13: 0,
# Unknown event
14: 0,
# Unknown event
15: 0,
# Unknown event
16: 0,
# Unknown event
17: 0,
# Unknown event
18: 0,
# Unknown event
19: 0,
}
stories = list(range(173))
root = Node.void('info22') for storyid in range(173):
for phaseid in phases: story = Node.void('story')
phase = Node.void('phase') root.add_child(story)
root.add_child(phase) story.add_child(Node.u32('story_id', storyid))
phase.add_child(Node.s16('event_id', phaseid)) story.add_child(Node.bool('is_limited', False))
phase.add_child(Node.s16('phase', phases[phaseid])) story.add_child(Node.u64('limit_date', 0))
for storyid in stories: return root
story = Node.void('story')
root.add_child(story)
story.add_child(Node.u32('story_id', storyid))
story.add_child(Node.bool('is_limited', False))
story.add_child(Node.u64('limit_date', 0))
return root def handle_pcb22_boot_request(self, request: Node) -> Node:
return Node.void('pcb22')
# Invalid method def handle_pcb22_error_request(self, request: Node) -> Node:
return None return Node.void('pcb22')
def handle_pcb22_request(self, request: Node) -> Optional[Node]: def handle_pcb22_write_request(self, request: Node) -> Node:
method = request.attribute('method') # Update the name of this cab for admin purposes
self.update_machine_name(request.child_value('pcb_setting/name'))
return Node.void('pcb22')
if method == 'boot': def handle_lobby22_request(self, request: Node) -> Node:
return Node.void('pcb22')
elif method == 'error':
return Node.void('pcb22')
elif method == 'write':
# Update the name of this cab for admin purposes
self.update_machine_name(request.child_value('pcb_setting/name'))
return Node.void('pcb22')
# Invalid method
return None
def handle_lobby22_request(self, request: Node) -> Optional[Node]:
# Stub out the entire lobby22 service # Stub out the entire lobby22 service
return Node.void('lobby22') return Node.void('lobby22')
def handle_player22_request(self, request: Node) -> Optional[Node]: def handle_player22_read_request(self, request: Node) -> Node:
method = request.attribute('method') refid = request.child_value('ref_id')
# Pop'n Music 22 doesn't send a modelstring to load old profiles,
if method == 'read': # it just expects us to know. So always look for old profiles in
refid = request.child_value('ref_id') # Pop'n 22 land.
# Pop'n Music 22 doesn't send a modelstring to load old profiles, root = self.get_profile_by_refid(refid, self.OLD_PROFILE_FALLTHROUGH)
# it just expects us to know. So always look for old profiles in if root is None:
# Pop'n 22 land.
root = self.get_profile_by_refid(refid, self.OLD_PROFILE_FALLTHROUGH)
if root is None:
root = Node.void('player22')
root.set_attribute('status', str(Status.NO_PROFILE))
return root
elif method == 'new':
refid = request.child_value('ref_id')
name = request.child_value('name')
root = self.new_profile_by_refid(refid, name)
if root is None:
root = Node.void('player22')
root.set_attribute('status', str(Status.NO_PROFILE))
return root
elif method == 'start':
return Node.void('player22')
elif method == 'logout':
return Node.void('player22')
elif method == 'write':
refid = request.child_value('ref_id')
root = Node.void('player22') root = Node.void('player22')
if refid is None: root.set_attribute('status', str(Status.NO_PROFILE))
return root 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 Profile(self.game, self.version, refid, 0)
newprofile = self.unformat_profile(userid, request, oldprofile)
if newprofile is not None:
self.put_profile(userid, newprofile)
return root
elif method == 'friend':
refid = request.attribute('ref_id')
no = int(request.attribute('no', '-1'))
def handle_player22_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('player22') root = Node.void('player22')
if no < 0: root.set_attribute('status', str(Status.NO_PROFILE))
root.add_child(Node.s8('result', 2)) return root
return root
# Look up our own user ID based on the RefID provided. def handle_player22_start_request(self, request: Node) -> Node:
userid = self.data.remote.user.from_refid(self.game, self.version, refid) return Node.void('player22')
if userid is None:
root.add_child(Node.s8('result', 2))
return root
# Grab the links that we care about. def handle_player22_logout_request(self, request: Node) -> Node:
links = self.data.local.user.get_links(self.game, self.version, userid) return Node.void('player22')
profiles: Dict[UserID, Profile] = {}
rivals: List[Link] = []
for link in links:
if link.type != 'rival':
continue
other_profile = self.get_profile(link.other_userid) def handle_player22_write_request(self, request: Node) -> Node:
if other_profile is None: refid = request.child_value('ref_id')
continue
profiles[link.other_userid] = other_profile
rivals.append(link)
# Somehow requested an invalid profile.
if no >= len(rivals):
root.add_child(Node.s8('result', 2))
return root
rivalid = links[no].other_userid
rivalprofile = profiles[rivalid]
scores = self.data.remote.music.get_scores(self.game, self.version, rivalid)
# First, output general profile info.
friend = Node.void('friend')
root.add_child(friend)
friend.add_child(Node.s16('no', no))
friend.add_child(Node.string('g_pm_id', ID.format_extid(rivalprofile.extid)))
friend.add_child(Node.string('name', rivalprofile.get_str('name', 'なし')))
friend.add_child(Node.s16('chara', rivalprofile.get_int('chara', -1)))
# This might be for having non-active or non-confirmed friends, but setting to 0 makes the
# ranking numbers disappear and the player icon show a questionmark.
friend.add_child(Node.s8('is_open', 1))
for score in scores:
# Skip any scores for chart types we don't support
if score.chart not in [
self.CHART_TYPE_EASY,
self.CHART_TYPE_NORMAL,
self.CHART_TYPE_HYPER,
self.CHART_TYPE_EX,
]:
continue
points = score.points
medal = score.data.get_int('medal')
music = Node.void('music')
friend.add_child(music)
music.set_attribute('music_num', str(score.id))
music.set_attribute('sheet_num', str({
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY,
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL,
self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER,
self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX,
}[score.chart]))
music.set_attribute('score', str(points))
music.set_attribute('clearmedal', str({
self.PLAY_MEDAL_CIRCLE_FAILED: self.GAME_PLAY_MEDAL_CIRCLE_FAILED,
self.PLAY_MEDAL_DIAMOND_FAILED: self.GAME_PLAY_MEDAL_DIAMOND_FAILED,
self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED,
self.PLAY_MEDAL_EASY_CLEAR: self.GAME_PLAY_MEDAL_EASY_CLEAR,
self.PLAY_MEDAL_CIRCLE_CLEARED: self.GAME_PLAY_MEDAL_CIRCLE_CLEARED,
self.PLAY_MEDAL_DIAMOND_CLEARED: self.GAME_PLAY_MEDAL_DIAMOND_CLEARED,
self.PLAY_MEDAL_STAR_CLEARED: self.GAME_PLAY_MEDAL_STAR_CLEARED,
self.PLAY_MEDAL_CIRCLE_FULL_COMBO: self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO,
self.PLAY_MEDAL_DIAMOND_FULL_COMBO: self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO,
self.PLAY_MEDAL_STAR_FULL_COMBO: self.GAME_PLAY_MEDAL_STAR_FULL_COMBO,
self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT,
}[medal]))
root = Node.void('player22')
if refid is None:
return root return root
elif method == 'conversion': userid = self.data.remote.user.from_refid(self.game, self.version, refid)
refid = request.child_value('ref_id') if userid is None:
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 return root
elif method == 'write_music': oldprofile = self.get_profile(userid) or Profile(self.game, self.version, refid, 0)
refid = request.child_value('ref_id') newprofile = self.unformat_profile(userid, request, oldprofile)
root = Node.void('player22') if newprofile is not None:
if refid is None: self.put_profile(userid, newprofile)
return root
userid = self.data.remote.user.from_refid(self.game, self.version, refid) return root
if userid is None:
return root
songid = request.child_value('music_num') def handle_player22_friend_request(self, request: Node) -> Node:
chart = { refid = request.attribute('ref_id')
self.GAME_CHART_TYPE_EASY: self.CHART_TYPE_EASY, no = int(request.attribute('no', '-1'))
self.GAME_CHART_TYPE_NORMAL: self.CHART_TYPE_NORMAL,
self.GAME_CHART_TYPE_HYPER: self.CHART_TYPE_HYPER, root = Node.void('player22')
self.GAME_CHART_TYPE_EX: self.CHART_TYPE_EX, if no < 0:
}[request.child_value('sheet_num')] root.add_child(Node.s8('result', 2))
medal = request.child_value('clearmedal')
points = request.child_value('score')
combo = request.child_value('combo')
stats = {
'cool': request.child_value('cool'),
'great': request.child_value('great'),
'good': request.child_value('good'),
'bad': request.child_value('bad')
}
medal = {
self.GAME_PLAY_MEDAL_CIRCLE_FAILED: self.PLAY_MEDAL_CIRCLE_FAILED,
self.GAME_PLAY_MEDAL_DIAMOND_FAILED: self.PLAY_MEDAL_DIAMOND_FAILED,
self.GAME_PLAY_MEDAL_STAR_FAILED: self.PLAY_MEDAL_STAR_FAILED,
self.GAME_PLAY_MEDAL_EASY_CLEAR: self.PLAY_MEDAL_EASY_CLEAR,
self.GAME_PLAY_MEDAL_CIRCLE_CLEARED: self.PLAY_MEDAL_CIRCLE_CLEARED,
self.GAME_PLAY_MEDAL_DIAMOND_CLEARED: self.PLAY_MEDAL_DIAMOND_CLEARED,
self.GAME_PLAY_MEDAL_STAR_CLEARED: self.PLAY_MEDAL_STAR_CLEARED,
self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO: self.PLAY_MEDAL_CIRCLE_FULL_COMBO,
self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO: self.PLAY_MEDAL_DIAMOND_FULL_COMBO,
self.GAME_PLAY_MEDAL_STAR_FULL_COMBO: self.PLAY_MEDAL_STAR_FULL_COMBO,
self.GAME_PLAY_MEDAL_PERFECT: self.PLAY_MEDAL_PERFECT,
}[medal]
self.update_score(userid, songid, chart, points, medal, combo=combo, stats=stats)
return root return root
# Invalid method # Look up our own user ID based on the RefID provided.
return None userid = self.data.remote.user.from_refid(self.game, self.version, refid)
if userid is None:
root.add_child(Node.s8('result', 2))
return root
# Grab the links that we care about.
links = self.data.local.user.get_links(self.game, self.version, userid)
profiles: Dict[UserID, 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)
# Somehow requested an invalid profile.
if no >= len(rivals):
root.add_child(Node.s8('result', 2))
return root
rivalid = links[no].other_userid
rivalprofile = profiles[rivalid]
scores = self.data.remote.music.get_scores(self.game, self.version, rivalid)
# First, output general profile info.
friend = Node.void('friend')
root.add_child(friend)
friend.add_child(Node.s16('no', no))
friend.add_child(Node.string('g_pm_id', ID.format_extid(rivalprofile.extid)))
friend.add_child(Node.string('name', rivalprofile.get_str('name', 'なし')))
friend.add_child(Node.s16('chara', rivalprofile.get_int('chara', -1)))
# This might be for having non-active or non-confirmed friends, but setting to 0 makes the
# ranking numbers disappear and the player icon show a questionmark.
friend.add_child(Node.s8('is_open', 1))
for score in scores:
# Skip any scores for chart types we don't support
if score.chart not in [
self.CHART_TYPE_EASY,
self.CHART_TYPE_NORMAL,
self.CHART_TYPE_HYPER,
self.CHART_TYPE_EX,
]:
continue
points = score.points
medal = score.data.get_int('medal')
music = Node.void('music')
friend.add_child(music)
music.set_attribute('music_num', str(score.id))
music.set_attribute('sheet_num', str({
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY,
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL,
self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER,
self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX,
}[score.chart]))
music.set_attribute('score', str(points))
music.set_attribute('clearmedal', str({
self.PLAY_MEDAL_CIRCLE_FAILED: self.GAME_PLAY_MEDAL_CIRCLE_FAILED,
self.PLAY_MEDAL_DIAMOND_FAILED: self.GAME_PLAY_MEDAL_DIAMOND_FAILED,
self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED,
self.PLAY_MEDAL_EASY_CLEAR: self.GAME_PLAY_MEDAL_EASY_CLEAR,
self.PLAY_MEDAL_CIRCLE_CLEARED: self.GAME_PLAY_MEDAL_CIRCLE_CLEARED,
self.PLAY_MEDAL_DIAMOND_CLEARED: self.GAME_PLAY_MEDAL_DIAMOND_CLEARED,
self.PLAY_MEDAL_STAR_CLEARED: self.GAME_PLAY_MEDAL_STAR_CLEARED,
self.PLAY_MEDAL_CIRCLE_FULL_COMBO: self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO,
self.PLAY_MEDAL_DIAMOND_FULL_COMBO: self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO,
self.PLAY_MEDAL_STAR_FULL_COMBO: self.GAME_PLAY_MEDAL_STAR_FULL_COMBO,
self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT,
}[medal]))
return root
def handle_player22_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
def handle_player22_write_music_request(self, request: Node) -> Node:
refid = request.child_value('ref_id')
root = Node.void('player22')
if refid is None:
return root
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
if userid is None:
return root
songid = request.child_value('music_num')
chart = {
self.GAME_CHART_TYPE_EASY: self.CHART_TYPE_EASY,
self.GAME_CHART_TYPE_NORMAL: self.CHART_TYPE_NORMAL,
self.GAME_CHART_TYPE_HYPER: self.CHART_TYPE_HYPER,
self.GAME_CHART_TYPE_EX: self.CHART_TYPE_EX,
}[request.child_value('sheet_num')]
medal = request.child_value('clearmedal')
points = request.child_value('score')
combo = request.child_value('combo')
stats = {
'cool': request.child_value('cool'),
'great': request.child_value('great'),
'good': request.child_value('good'),
'bad': request.child_value('bad')
}
medal = {
self.GAME_PLAY_MEDAL_CIRCLE_FAILED: self.PLAY_MEDAL_CIRCLE_FAILED,
self.GAME_PLAY_MEDAL_DIAMOND_FAILED: self.PLAY_MEDAL_DIAMOND_FAILED,
self.GAME_PLAY_MEDAL_STAR_FAILED: self.PLAY_MEDAL_STAR_FAILED,
self.GAME_PLAY_MEDAL_EASY_CLEAR: self.PLAY_MEDAL_EASY_CLEAR,
self.GAME_PLAY_MEDAL_CIRCLE_CLEARED: self.PLAY_MEDAL_CIRCLE_CLEARED,
self.GAME_PLAY_MEDAL_DIAMOND_CLEARED: self.PLAY_MEDAL_DIAMOND_CLEARED,
self.GAME_PLAY_MEDAL_STAR_CLEARED: self.PLAY_MEDAL_STAR_CLEARED,
self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO: self.PLAY_MEDAL_CIRCLE_FULL_COMBO,
self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO: self.PLAY_MEDAL_DIAMOND_FULL_COMBO,
self.GAME_PLAY_MEDAL_STAR_FULL_COMBO: self.PLAY_MEDAL_STAR_FULL_COMBO,
self.GAME_PLAY_MEDAL_PERFECT: self.PLAY_MEDAL_PERFECT,
}[medal]
self.update_score(userid, songid, chart, points, medal, combo=combo, stats=stats)
return root
def format_profile(self, userid: UserID, profile: Profile) -> Node: def format_profile(self, userid: UserID, profile: Profile) -> Node:
root = Node.void('player22') root = Node.void('player22')

View File

@ -1,6 +1,4 @@
# vim: set fileencoding=utf-8 # vim: set fileencoding=utf-8
from typing import Optional
from bemani.backend.popn.base import PopnMusicBase from bemani.backend.popn.base import PopnMusicBase
from bemani.backend.popn.usaneko import PopnMusicUsaNeko from bemani.backend.popn.usaneko import PopnMusicUsaNeko
from bemani.common import VersionConstants from bemani.common import VersionConstants
@ -11,5 +9,5 @@ class PopnMusicPeace(PopnMusicBase):
name = "Pop'n Music peace" name = "Pop'n Music peace"
version = VersionConstants.POPN_MUSIC_PEACE version = VersionConstants.POPN_MUSIC_PEACE
def previous_version(self) -> Optional[PopnMusicBase]: def previous_version(self) -> PopnMusicBase:
return PopnMusicUsaNeko(self.data, self.config, self.model) return PopnMusicUsaNeko(self.data, self.config, self.model)

View File

@ -48,7 +48,7 @@ class PopnMusicUsaNeko(PopnMusicBase):
# Biggest ID in the music DB # Biggest ID in the music DB
GAME_MAX_MUSIC_ID = 1704 GAME_MAX_MUSIC_ID = 1704
def previous_version(self) -> Optional[PopnMusicBase]: def previous_version(self) -> PopnMusicBase:
return PopnMusicEclale(self.data, self.config, self.model) return PopnMusicEclale(self.data, self.config, self.model)
def extra_services(self) -> List[str]: def extra_services(self) -> List[str]:
@ -111,7 +111,7 @@ class PopnMusicUsaNeko(PopnMusicBase):
return self.GAME_PLAY_RANK_AAA return self.GAME_PLAY_RANK_AAA
return self.GAME_PLAY_RANK_S return self.GAME_PLAY_RANK_S
def handle_lobby24_request(self, request: Node) -> Optional[Node]: def handle_lobby24_request(self, request: Node) -> Node:
# Stub out the entire lobby24 service # Stub out the entire lobby24 service
return Node.void('lobby24') return Node.void('lobby24')