Merge remote-tracking branch 'upstream/trunk' into trunk
This commit is contained in:
commit
ede06b3e68
@ -1,6 +1,7 @@
|
|||||||
from abc import ABC
|
from abc import ABC, abstractmethod
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type
|
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type
|
||||||
|
from typing_extensions import Final
|
||||||
|
|
||||||
from bemani.common import Model, ValidatedDict, Profile, PlayStatistics, GameConstants, Time
|
from bemani.common import Model, ValidatedDict, Profile, PlayStatistics, GameConstants, Time
|
||||||
from bemani.data import Config, Data, Machine, UserID, RemoteUser
|
from bemani.data import Config, Data, Machine, UserID, RemoteUser
|
||||||
@ -14,14 +15,14 @@ class Status:
|
|||||||
"""
|
"""
|
||||||
List of statuses we return to the game for various reasons.
|
List of statuses we return to the game for various reasons.
|
||||||
"""
|
"""
|
||||||
SUCCESS = 0
|
SUCCESS: Final[int] = 0
|
||||||
NO_PROFILE = 109
|
NO_PROFILE: Final[int] = 109
|
||||||
NOT_ALLOWED = 110
|
NOT_ALLOWED: Final[int] = 110
|
||||||
NOT_REGISTERED = 112
|
NOT_REGISTERED: Final[int] = 112
|
||||||
INVALID_PIN = 116
|
INVALID_PIN: Final[int] = 116
|
||||||
|
|
||||||
|
|
||||||
class Factory:
|
class Factory(ABC):
|
||||||
"""
|
"""
|
||||||
The base class every game factory inherits from. Defines a create method
|
The base class every game factory inherits from. Defines a create method
|
||||||
which should return some game class which can handle packets. Game classes
|
which should return some game class which can handle packets. Game classes
|
||||||
@ -29,16 +30,17 @@ class Factory:
|
|||||||
Dispatch will look up in order to handle calls.
|
Dispatch will look up in order to handle calls.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MANAGED_CLASSES: List[Type["Base"]] = []
|
MANAGED_CLASSES: List[Type["Base"]]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@abstractmethod
|
||||||
def register_all(cls) -> None:
|
def register_all(cls) -> None:
|
||||||
"""
|
"""
|
||||||
Subclasses of this class should use this function to register themselves
|
Subclasses of this class should use this function to register themselves
|
||||||
with Base, using Base.register(). Factories specify the game code that
|
with Base, using Base.register(). Factories specify the game code that
|
||||||
they support, which Base will use when routing requests.
|
they support, which Base will use when routing requests.
|
||||||
"""
|
"""
|
||||||
raise Exception('Override this in subclass!')
|
raise NotImplementedError('Override this in subclass!')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run_scheduled_work(cls, data: Data, config: Config) -> None:
|
def run_scheduled_work(cls, data: Data, config: Config) -> None:
|
||||||
@ -84,6 +86,7 @@ class Factory:
|
|||||||
yield (game.game, game.version, game.get_settings())
|
yield (game.game, game.version, game.get_settings())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@abstractmethod
|
||||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional['Base']:
|
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional['Base']:
|
||||||
"""
|
"""
|
||||||
Given a modelstring and an optional parent model, return an instantiated game class that can handle a packet.
|
Given a modelstring and an optional parent model, return an instantiated game class that can handle a packet.
|
||||||
@ -102,7 +105,7 @@ class Factory:
|
|||||||
A subclass of Base that hopefully has a handle_<call>_request method on it, for the particular
|
A subclass of Base that hopefully has a handle_<call>_request method on it, for the particular
|
||||||
call that Dispatch wants to resolve, or None if we can't look up a game.
|
call that Dispatch wants to resolve, or None if we can't look up a game.
|
||||||
"""
|
"""
|
||||||
raise Exception('Override this in subclass!')
|
raise NotImplementedError('Override this in subclass!')
|
||||||
|
|
||||||
|
|
||||||
class Base(ABC):
|
class Base(ABC):
|
||||||
|
@ -18,6 +18,7 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
game = GameConstants.POPN_MUSIC
|
game = GameConstants.POPN_MUSIC
|
||||||
|
|
||||||
# Play medals, as saved into/loaded from the DB
|
# Play medals, as saved into/loaded from the DB
|
||||||
|
PLAY_MEDAL_NO_PLAY = DBConstants.POPN_MUSIC_PLAY_MEDAL_NO_PLAY
|
||||||
PLAY_MEDAL_CIRCLE_FAILED = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED
|
PLAY_MEDAL_CIRCLE_FAILED = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED
|
||||||
PLAY_MEDAL_DIAMOND_FAILED = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED
|
PLAY_MEDAL_DIAMOND_FAILED = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED
|
||||||
PLAY_MEDAL_STAR_FAILED = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED
|
PLAY_MEDAL_STAR_FAILED = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED
|
||||||
@ -188,6 +189,7 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
"""
|
"""
|
||||||
# Range check medals
|
# Range check medals
|
||||||
if medal not in [
|
if medal not in [
|
||||||
|
self.PLAY_MEDAL_NO_PLAY,
|
||||||
self.PLAY_MEDAL_CIRCLE_FAILED,
|
self.PLAY_MEDAL_CIRCLE_FAILED,
|
||||||
self.PLAY_MEDAL_DIAMOND_FAILED,
|
self.PLAY_MEDAL_DIAMOND_FAILED,
|
||||||
self.PLAY_MEDAL_STAR_FAILED,
|
self.PLAY_MEDAL_STAR_FAILED,
|
||||||
|
@ -276,6 +276,8 @@ class PopnMusicEclale(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
points = score.points
|
points = score.points
|
||||||
medal = score.data.get_int('medal')
|
medal = score.data.get_int('medal')
|
||||||
@ -364,6 +366,8 @@ class PopnMusicEclale(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
points = score.points
|
points = score.points
|
||||||
medal = score.data.get_int('medal')
|
medal = score.data.get_int('medal')
|
||||||
@ -458,6 +462,8 @@ class PopnMusicEclale(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
music = Node.void('music')
|
music = Node.void('music')
|
||||||
root.add_child(music)
|
root.add_child(music)
|
||||||
|
@ -198,6 +198,8 @@ class PopnMusicFantasia(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
||||||
hiscore_index = (score.id * 4) + {
|
hiscore_index = (score.id * 4) + {
|
||||||
@ -289,6 +291,8 @@ class PopnMusicFantasia(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
||||||
hiscore_index = (score.id * 4) + {
|
hiscore_index = (score.id * 4) + {
|
||||||
@ -567,6 +571,8 @@ class PopnMusicFantasia(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
||||||
hiscore_index = (score.id * 4) + {
|
hiscore_index = (score.id * 4) + {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
# vim: set fileencoding=utf-8
|
# vim: set fileencoding=utf-8
|
||||||
import copy
|
import copy
|
||||||
from typing import Dict, List
|
from typing import Any, 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
|
||||||
|
|
||||||
from bemani.backend.base import Status
|
from bemani.backend.base import Status
|
||||||
from bemani.common import Profile, VersionConstants, ID
|
from bemani.common import ValidatedDict, Profile, VersionConstants, ID
|
||||||
from bemani.data import UserID, Link
|
from bemani.data import UserID, Link
|
||||||
from bemani.protocol import Node
|
from bemani.protocol import Node
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
GAME_CHART_TYPE_EX = 3
|
GAME_CHART_TYPE_EX = 3
|
||||||
|
|
||||||
# Medal type, as returned from the game
|
# Medal type, as returned from the game
|
||||||
|
GAME_PLAY_MEDAL_NO_PLAY = 0
|
||||||
GAME_PLAY_MEDAL_CIRCLE_FAILED = 1
|
GAME_PLAY_MEDAL_CIRCLE_FAILED = 1
|
||||||
GAME_PLAY_MEDAL_DIAMOND_FAILED = 2
|
GAME_PLAY_MEDAL_DIAMOND_FAILED = 2
|
||||||
GAME_PLAY_MEDAL_STAR_FAILED = 3
|
GAME_PLAY_MEDAL_STAR_FAILED = 3
|
||||||
@ -41,48 +42,101 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
def previous_version(self) -> PopnMusicBase:
|
def previous_version(self) -> PopnMusicBase:
|
||||||
return PopnMusicSunnyPark(self.data, self.config, self.model)
|
return PopnMusicSunnyPark(self.data, self.config, self.model)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_settings(cls) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Return all of our front-end modifiably settings.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'ints': [
|
||||||
|
{
|
||||||
|
'name': 'Story Mode',
|
||||||
|
'tip': 'Story mode phase for all players.',
|
||||||
|
'category': 'game_config',
|
||||||
|
'setting': 'story_phase',
|
||||||
|
'values': {
|
||||||
|
0: 'Disabled',
|
||||||
|
1: 'Phase 1',
|
||||||
|
2: 'Phase 2',
|
||||||
|
3: 'Phase 3',
|
||||||
|
4: 'Phase 4',
|
||||||
|
5: 'Phase 5',
|
||||||
|
6: 'Phase 6',
|
||||||
|
7: 'Phase 7',
|
||||||
|
8: 'Phase 8',
|
||||||
|
9: 'Phase 9',
|
||||||
|
10: 'Phase 10',
|
||||||
|
11: 'Phase 11',
|
||||||
|
12: 'Phase 12',
|
||||||
|
13: 'Phase 13',
|
||||||
|
14: 'Phase 14',
|
||||||
|
15: 'Phase 15',
|
||||||
|
16: 'Phase 16',
|
||||||
|
17: 'Phase 17',
|
||||||
|
18: 'Phase 18',
|
||||||
|
19: 'Phase 19',
|
||||||
|
20: 'Phase 20',
|
||||||
|
21: 'Phase 21',
|
||||||
|
22: 'Phase 22',
|
||||||
|
23: 'Phase 23',
|
||||||
|
24: 'Phase 24',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'bools': [
|
||||||
|
{
|
||||||
|
'name': 'Force Song Unlock',
|
||||||
|
'tip': 'Force unlock all songs.',
|
||||||
|
'category': 'game_config',
|
||||||
|
'setting': 'force_unlock_songs',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
def handle_info22_common_request(self, request: Node) -> Node:
|
def handle_info22_common_request(self, request: Node) -> Node:
|
||||||
# TODO: Hook these up to config so we can change this
|
game_config = self.get_game_config()
|
||||||
|
story_phase = game_config.get_int('story_phase')
|
||||||
|
|
||||||
phases = {
|
phases = {
|
||||||
# Unknown event (0-16)
|
# Default song phase availability (0-16)
|
||||||
0: 0,
|
0: 16,
|
||||||
# Unknown event (0-11)
|
# Card phase (0-11)
|
||||||
1: 0,
|
1: 11,
|
||||||
# Pop'n Aura, max (0-11) (remove all aura requirements)
|
# Pop'n Aura, max (0-11) (remove all aura requirements)
|
||||||
2: 11,
|
2: 11,
|
||||||
# Story (0-24)
|
# Story (0-24)
|
||||||
3: 1,
|
3: story_phase,
|
||||||
# BEMANI ruins Discovery! (0-2)
|
# BEMANI ruins Discovery! 0 = off, 1 = active, 2 = off
|
||||||
4: 0,
|
4: 0,
|
||||||
# Unknown event, something to do with net taisen (0-2)
|
# Unknown event, something to do with net taisen (0-2)
|
||||||
5: 0,
|
5: 2,
|
||||||
# Unknown event (0-1)
|
# Unknown event (0-1)
|
||||||
6: 0,
|
6: 1,
|
||||||
# Unknown event (0-1)
|
# Unknown event (0-1)
|
||||||
7: 0,
|
7: 1,
|
||||||
# Unknown event (0-1)
|
# Unknown event (0-1)
|
||||||
8: 0,
|
8: 1,
|
||||||
# Unknown event (0-11)
|
# Course mode phase (0-11)
|
||||||
9: 0,
|
9: 11,
|
||||||
# Unknown event (0-2)
|
# Pon's Fate Purification Plan, 0 = off, 1 = active, 2 = off
|
||||||
10: 0,
|
10: 0,
|
||||||
# Unknown event (0-3)
|
# Unknown event (0-3)
|
||||||
11: 0,
|
11: 3,
|
||||||
# Unknown event (0-1)
|
# Unknown event (0-1)
|
||||||
12: 0,
|
12: 1,
|
||||||
# Unknown event (0-2)
|
# Appears to be unlocks for course mode including KAC stuff.
|
||||||
13: 0,
|
13: 2,
|
||||||
# Unknown event (0-4)
|
# Unknown event (0-4)
|
||||||
14: 0,
|
14: 4,
|
||||||
# Unknown event (0-2)
|
# Unknown event (0-2)
|
||||||
15: 0,
|
15: 2,
|
||||||
# Unknown event (0-2)
|
# Unknown event (0-2)
|
||||||
16: 0,
|
16: 2,
|
||||||
# Unknown event (0-12)
|
# Unknown event (0-12)
|
||||||
17: 0,
|
17: 0,
|
||||||
# Unknown event (0-2)
|
# Unknown event (0-2)
|
||||||
18: 0,
|
18: 2,
|
||||||
# Unknown event (0-7)
|
# Bemani Summer Diary, 0 = off, 1-6 are phases, 7 = off
|
||||||
19: 0,
|
19: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,6 +252,7 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
rivalid = links[no].other_userid
|
rivalid = links[no].other_userid
|
||||||
rivalprofile = profiles[rivalid]
|
rivalprofile = profiles[rivalid]
|
||||||
scores = self.data.remote.music.get_scores(self.game, self.version, rivalid)
|
scores = self.data.remote.music.get_scores(self.game, self.version, rivalid)
|
||||||
|
achievements = self.data.local.user.get_achievements(self.game, self.version, rivalid)
|
||||||
|
|
||||||
# First, output general profile info.
|
# First, output general profile info.
|
||||||
friend = Node.void('friend')
|
friend = Node.void('friend')
|
||||||
@ -234,6 +289,7 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
}[score.chart]))
|
}[score.chart]))
|
||||||
music.set_attribute('score', str(points))
|
music.set_attribute('score', str(points))
|
||||||
music.set_attribute('clearmedal', str({
|
music.set_attribute('clearmedal', str({
|
||||||
|
self.PLAY_MEDAL_NO_PLAY: self.GAME_PLAY_MEDAL_NO_PLAY,
|
||||||
self.PLAY_MEDAL_CIRCLE_FAILED: self.GAME_PLAY_MEDAL_CIRCLE_FAILED,
|
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_DIAMOND_FAILED: self.GAME_PLAY_MEDAL_DIAMOND_FAILED,
|
||||||
self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED,
|
self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED,
|
||||||
@ -247,6 +303,27 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT,
|
self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT,
|
||||||
}[medal]))
|
}[medal]))
|
||||||
|
|
||||||
|
for course in achievements:
|
||||||
|
if course.type == 'course':
|
||||||
|
total_score = course.data.get_int('total_score')
|
||||||
|
clear_medal = course.data.get_int('clear_medal')
|
||||||
|
clear_norma = course.data.get_int('clear_norma')
|
||||||
|
stage1_score = course.data.get_int('stage1_score')
|
||||||
|
stage2_score = course.data.get_int('stage2_score')
|
||||||
|
stage3_score = course.data.get_int('stage3_score')
|
||||||
|
stage4_score = course.data.get_int('stage4_score')
|
||||||
|
|
||||||
|
coursenode = Node.void('course')
|
||||||
|
friend.add_child(coursenode)
|
||||||
|
coursenode.set_attribute('course_id', str(course.id))
|
||||||
|
coursenode.set_attribute('clear_medal', str(clear_medal))
|
||||||
|
coursenode.set_attribute('clear_norma', str(clear_norma))
|
||||||
|
coursenode.set_attribute('stage1_score', str(stage1_score))
|
||||||
|
coursenode.set_attribute('stage2_score', str(stage2_score))
|
||||||
|
coursenode.set_attribute('stage3_score', str(stage3_score))
|
||||||
|
coursenode.set_attribute('stage4_score', str(stage4_score))
|
||||||
|
coursenode.set_attribute('total_score', str(total_score))
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_player22_conversion_request(self, request: Node) -> Node:
|
def handle_player22_conversion_request(self, request: Node) -> Node:
|
||||||
@ -287,6 +364,7 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
'bad': request.child_value('bad')
|
'bad': request.child_value('bad')
|
||||||
}
|
}
|
||||||
medal = {
|
medal = {
|
||||||
|
self.GAME_PLAY_MEDAL_NO_PLAY: self.PLAY_MEDAL_NO_PLAY,
|
||||||
self.GAME_PLAY_MEDAL_CIRCLE_FAILED: self.PLAY_MEDAL_CIRCLE_FAILED,
|
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_DIAMOND_FAILED: self.PLAY_MEDAL_DIAMOND_FAILED,
|
||||||
self.GAME_PLAY_MEDAL_STAR_FAILED: self.PLAY_MEDAL_STAR_FAILED,
|
self.GAME_PLAY_MEDAL_STAR_FAILED: self.PLAY_MEDAL_STAR_FAILED,
|
||||||
@ -302,6 +380,119 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
self.update_score(userid, songid, chart, points, medal, combo=combo, stats=stats)
|
self.update_score(userid, songid, chart, points, medal, combo=combo, stats=stats)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
def handle_player22_write_course_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
|
||||||
|
|
||||||
|
# Grab info that we want to update
|
||||||
|
total_score = request.child_value('total_score') or 0
|
||||||
|
course_id = request.child_value('course_id')
|
||||||
|
if course_id is not None:
|
||||||
|
machine = self.data.local.machine.get_machine(self.config.machine.pcbid)
|
||||||
|
pref = request.child_value('pref') or 51
|
||||||
|
profile = self.get_profile(userid) or Profile(self.game, self.version, refid, 0)
|
||||||
|
|
||||||
|
course = self.data.local.user.get_achievement(
|
||||||
|
self.game,
|
||||||
|
self.version,
|
||||||
|
userid,
|
||||||
|
course_id,
|
||||||
|
"course",
|
||||||
|
) or ValidatedDict({})
|
||||||
|
|
||||||
|
stage_scores: Dict[int, int] = {}
|
||||||
|
for child in request.children:
|
||||||
|
if child.name != 'stage':
|
||||||
|
continue
|
||||||
|
|
||||||
|
stage = child.child_value('stage')
|
||||||
|
score = child.child_value('score')
|
||||||
|
|
||||||
|
if isinstance(stage, int) and isinstance(score, int):
|
||||||
|
stage_scores[stage] = score
|
||||||
|
|
||||||
|
# Update the scores if this was a new high score.
|
||||||
|
if total_score > course.get_int('total_score'):
|
||||||
|
course.replace_int('total_score', total_score)
|
||||||
|
course.replace_int('stage1_score', stage_scores.get(0, 0))
|
||||||
|
course.replace_int('stage2_score', stage_scores.get(1, 0))
|
||||||
|
course.replace_int('stage3_score', stage_scores.get(2, 0))
|
||||||
|
course.replace_int('stage4_score', stage_scores.get(3, 0))
|
||||||
|
|
||||||
|
# Only update ojamas used if this was an updated score.
|
||||||
|
course.replace_int('clear_norma', request.child_value('clear_norma'))
|
||||||
|
|
||||||
|
# Only udpate what location and prefecture this was scored in
|
||||||
|
# if we updated our score.
|
||||||
|
course.replace_int('pref', pref)
|
||||||
|
course.replace_int('lid', machine.arcade)
|
||||||
|
|
||||||
|
# Update medal and combo values.
|
||||||
|
course.replace_int('max_combo', max(course.get_int('max_combo'), request.child_value('max_combo')))
|
||||||
|
course.replace_int('clear_medal', max(course.get_int('clear_medal'), request.child_value('clear_medal')))
|
||||||
|
|
||||||
|
# Add one to the play count for this course.
|
||||||
|
course.increment_int('play_cnt')
|
||||||
|
|
||||||
|
self.data.local.user.put_achievement(
|
||||||
|
self.game,
|
||||||
|
self.version,
|
||||||
|
userid,
|
||||||
|
course_id,
|
||||||
|
"course",
|
||||||
|
course,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Now, attempt to calculate ranking for this user for this run.
|
||||||
|
all_courses = self.data.local.user.get_all_achievements(self.game, self.version, course_id, "course")
|
||||||
|
global_ranking = sorted(all_courses, key=lambda entry: entry[1].data.get_int('total_score'), reverse=True)
|
||||||
|
pref_ranking = [c for c in global_ranking if c[1].data.get_int('pref') == pref]
|
||||||
|
local_ranking = [c for c in global_ranking if c[1].data.get_int('lid') == machine.arcade]
|
||||||
|
|
||||||
|
global_rank = len(global_ranking)
|
||||||
|
pref_rank = len(pref_ranking)
|
||||||
|
local_rank = len(local_ranking)
|
||||||
|
|
||||||
|
for i, rank in enumerate(global_ranking):
|
||||||
|
if userid == rank[0]:
|
||||||
|
global_rank = i + 1
|
||||||
|
break
|
||||||
|
for i, rank in enumerate(pref_ranking):
|
||||||
|
if userid == rank[0]:
|
||||||
|
pref_rank = i + 1
|
||||||
|
break
|
||||||
|
for i, rank in enumerate(local_ranking):
|
||||||
|
if userid == rank[0]:
|
||||||
|
local_rank = i + 1
|
||||||
|
break
|
||||||
|
|
||||||
|
# Now, return it all.
|
||||||
|
for rank_type, personal_rank, count in [
|
||||||
|
('all_ranking', global_rank, len(global_ranking)),
|
||||||
|
('pref_ranking', pref_rank, len(pref_ranking)),
|
||||||
|
('location_ranking', local_rank, len(local_ranking)),
|
||||||
|
]:
|
||||||
|
ranknode = Node.void(rank_type)
|
||||||
|
root.add_child(ranknode)
|
||||||
|
ranknode.add_child(Node.string('name', profile.get_str('name', 'なし')))
|
||||||
|
ranknode.add_child(Node.s16('chara_num', profile.get_int('chara', -1)))
|
||||||
|
ranknode.add_child(Node.s32('stage1_score', stage_scores.get(0, 0)))
|
||||||
|
ranknode.add_child(Node.s32('stage2_score', stage_scores.get(1, 0)))
|
||||||
|
ranknode.add_child(Node.s32('stage3_score', stage_scores.get(2, 0)))
|
||||||
|
ranknode.add_child(Node.s32('stage4_score', stage_scores.get(3, 0)))
|
||||||
|
ranknode.add_child(Node.s32('total_score', total_score))
|
||||||
|
ranknode.add_child(Node.s16('player_count', count))
|
||||||
|
ranknode.add_child(Node.s16('player_rank', personal_rank))
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
@ -380,6 +571,7 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
music.add_child(Node.s16('cnt', score.plays))
|
music.add_child(Node.s16('cnt', score.plays))
|
||||||
music.add_child(Node.s32('score', points))
|
music.add_child(Node.s32('score', points))
|
||||||
music.add_child(Node.u8('clear_type', {
|
music.add_child(Node.u8('clear_type', {
|
||||||
|
self.PLAY_MEDAL_NO_PLAY: self.GAME_PLAY_MEDAL_NO_PLAY,
|
||||||
self.PLAY_MEDAL_CIRCLE_FAILED: self.GAME_PLAY_MEDAL_CIRCLE_FAILED,
|
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_DIAMOND_FAILED: self.GAME_PLAY_MEDAL_DIAMOND_FAILED,
|
||||||
self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED,
|
self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED,
|
||||||
@ -474,6 +666,17 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
customize.add_child(Node.u16('comment_1', customize_dict.get_int('comment_1')))
|
customize.add_child(Node.u16('comment_1', customize_dict.get_int('comment_1')))
|
||||||
customize.add_child(Node.u16('comment_2', customize_dict.get_int('comment_2')))
|
customize.add_child(Node.u16('comment_2', customize_dict.get_int('comment_2')))
|
||||||
|
|
||||||
|
game_config = self.get_game_config()
|
||||||
|
if game_config.get_bool('force_unlock_songs'):
|
||||||
|
songs = self.data.local.music.get_all_songs(self.game, self.version)
|
||||||
|
for song in songs:
|
||||||
|
item = Node.void('item')
|
||||||
|
root.add_child(item)
|
||||||
|
item.add_child(Node.u8('type', 0))
|
||||||
|
item.add_child(Node.u16('id', song.id))
|
||||||
|
item.add_child(Node.u16('param', 15))
|
||||||
|
item.add_child(Node.bool('is_new', False))
|
||||||
|
|
||||||
# Set up achievements
|
# Set up achievements
|
||||||
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
|
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
|
||||||
for achievement in achievements:
|
for achievement in achievements:
|
||||||
@ -481,6 +684,20 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
itemtype = achievement.data.get_int('type')
|
itemtype = achievement.data.get_int('type')
|
||||||
param = achievement.data.get_int('param')
|
param = achievement.data.get_int('param')
|
||||||
|
|
||||||
|
# Maximum for each type is as follows:
|
||||||
|
# 0, 1423 - These are song unlocks as far as I can tell, matches Eclale/UsaNeko.
|
||||||
|
# 1, 2040
|
||||||
|
# 2, 510
|
||||||
|
# 3, 173
|
||||||
|
# 4, 40
|
||||||
|
# 5, 24
|
||||||
|
# 6, 24
|
||||||
|
# 7, 4158
|
||||||
|
|
||||||
|
if game_config.get_bool('force_unlock_songs') and itemtype == 0:
|
||||||
|
# We already sent song unlocks in the force unlock section above.
|
||||||
|
continue
|
||||||
|
|
||||||
item = Node.void('item')
|
item = Node.void('item')
|
||||||
root.add_child(item)
|
root.add_child(item)
|
||||||
item.add_child(Node.u8('type', itemtype))
|
item.add_child(Node.u8('type', itemtype))
|
||||||
@ -518,6 +735,34 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
story.add_child(Node.bool('is_cleared', cleared))
|
story.add_child(Node.bool('is_cleared', cleared))
|
||||||
story.add_child(Node.u32('clear_chapter', clear_chapter))
|
story.add_child(Node.u32('clear_chapter', clear_chapter))
|
||||||
|
|
||||||
|
elif achievement.type == 'course':
|
||||||
|
total_score = achievement.data.get_int('total_score')
|
||||||
|
max_combo = achievement.data.get_int('max_combo')
|
||||||
|
play_cnt = achievement.data.get_int('play_cnt')
|
||||||
|
clear_medal = achievement.data.get_int('clear_medal')
|
||||||
|
clear_norma = achievement.data.get_int('clear_norma')
|
||||||
|
stage1_score = achievement.data.get_int('stage1_score')
|
||||||
|
stage2_score = achievement.data.get_int('stage2_score')
|
||||||
|
stage3_score = achievement.data.get_int('stage3_score')
|
||||||
|
stage4_score = achievement.data.get_int('stage4_score')
|
||||||
|
|
||||||
|
course = Node.void('course')
|
||||||
|
root.add_child(course)
|
||||||
|
course.add_child(Node.s16('course_id', achievement.id))
|
||||||
|
course.add_child(Node.u8('clear_medal', clear_medal))
|
||||||
|
course.add_child(Node.u8('clear_norma', clear_norma))
|
||||||
|
course.add_child(Node.s32('stage1_score', stage1_score))
|
||||||
|
course.add_child(Node.s32('stage2_score', stage2_score))
|
||||||
|
course.add_child(Node.s32('stage3_score', stage3_score))
|
||||||
|
course.add_child(Node.s32('stage4_score', stage4_score))
|
||||||
|
course.add_child(Node.s32('total_score', total_score))
|
||||||
|
course.add_child(Node.s16('max_cmbo', max_combo)) # Yes, it is misspelled.
|
||||||
|
course.add_child(Node.s16('play_cnt', play_cnt))
|
||||||
|
course.add_child(Node.s16('all_rank', 1)) # Unclear what this does.
|
||||||
|
|
||||||
|
# There are also course_rank nodes, but it doesn't appear they get displayed
|
||||||
|
# to the user anywhere.
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||||
@ -584,6 +829,7 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
self.update_play_statistics(userid)
|
self.update_play_statistics(userid)
|
||||||
|
|
||||||
# Extract achievements
|
# Extract achievements
|
||||||
|
game_config = self.get_game_config()
|
||||||
for node in request.children:
|
for node in request.children:
|
||||||
if node.name == 'item':
|
if node.name == 'item':
|
||||||
if not node.child_value('is_new'):
|
if not node.child_value('is_new'):
|
||||||
@ -594,6 +840,10 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
itemtype = node.child_value('type')
|
itemtype = node.child_value('type')
|
||||||
param = node.child_value('param')
|
param = node.child_value('param')
|
||||||
|
|
||||||
|
if game_config.get_bool('force_unlock_songs') and itemtype == 0:
|
||||||
|
# If we enabled force song unlocks, don't save songs to the profile.
|
||||||
|
continue
|
||||||
|
|
||||||
self.data.local.user.put_achievement(
|
self.data.local.user.put_achievement(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
@ -698,6 +948,7 @@ class PopnMusicLapistoria(PopnMusicBase):
|
|||||||
music.add_child(Node.u8('clear_type', 0))
|
music.add_child(Node.u8('clear_type', 0))
|
||||||
music.add_child(Node.s32('old_score', points))
|
music.add_child(Node.s32('old_score', points))
|
||||||
music.add_child(Node.u8('old_clear_type', {
|
music.add_child(Node.u8('old_clear_type', {
|
||||||
|
self.PLAY_MEDAL_NO_PLAY: self.GAME_PLAY_MEDAL_NO_PLAY,
|
||||||
self.PLAY_MEDAL_CIRCLE_FAILED: self.GAME_PLAY_MEDAL_CIRCLE_FAILED,
|
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_DIAMOND_FAILED: self.GAME_PLAY_MEDAL_DIAMOND_FAILED,
|
||||||
self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED,
|
self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED,
|
||||||
|
@ -175,6 +175,8 @@ class PopnMusicSunnyPark(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
||||||
hiscore_index = (score.id * 4) + {
|
hiscore_index = (score.id * 4) + {
|
||||||
@ -348,6 +350,8 @@ class PopnMusicSunnyPark(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
||||||
|
|
||||||
@ -653,6 +657,8 @@ class PopnMusicSunnyPark(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
||||||
hiscore_index = (score.id * 4) + {
|
hiscore_index = (score.id * 4) + {
|
||||||
|
@ -238,6 +238,8 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EASY,
|
self.CHART_TYPE_EASY,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
flags = self.__format_flags_for_score(score)
|
flags = self.__format_flags_for_score(score)
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ class PopnMusicUsaNeko(PopnMusicBase):
|
|||||||
phase.add_child(Node.s16('event_id', phaseid))
|
phase.add_child(Node.s16('event_id', phaseid))
|
||||||
phase.add_child(Node.s16('phase', phase_value))
|
phase.add_child(Node.s16('phase', phase_value))
|
||||||
|
|
||||||
# Gather course informatino and course ranking for users.
|
# Gather course information and course ranking for users.
|
||||||
course_infos, achievements, profiles = Parallel.execute([
|
course_infos, achievements, profiles = Parallel.execute([
|
||||||
lambda: self.data.local.game.get_all_time_sensitive_settings(self.game, self.version, 'course'),
|
lambda: self.data.local.game.get_all_time_sensitive_settings(self.game, self.version, 'course'),
|
||||||
lambda: self.data.local.user.get_all_achievements(self.game, self.version),
|
lambda: self.data.local.user.get_all_achievements(self.game, self.version),
|
||||||
@ -555,6 +555,8 @@ class PopnMusicUsaNeko(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
points = score.points
|
points = score.points
|
||||||
medal = score.data.get_int('medal')
|
medal = score.data.get_int('medal')
|
||||||
@ -617,6 +619,8 @@ class PopnMusicUsaNeko(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
music = Node.void('music')
|
music = Node.void('music')
|
||||||
root.add_child(music)
|
root.add_child(music)
|
||||||
@ -757,6 +761,8 @@ class PopnMusicUsaNeko(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
|
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||||
|
continue
|
||||||
|
|
||||||
music = Node.void('music')
|
music = Node.void('music')
|
||||||
root.add_child(music)
|
root.add_child(music)
|
||||||
|
@ -94,6 +94,7 @@ class PopnMusicLapistoriaClient(BaseClient):
|
|||||||
# Extract and return score data
|
# Extract and return score data
|
||||||
medals: Dict[int, List[int]] = {}
|
medals: Dict[int, List[int]] = {}
|
||||||
scores: Dict[int, List[int]] = {}
|
scores: Dict[int, List[int]] = {}
|
||||||
|
courses: Dict[int, Dict[str, int]] = {}
|
||||||
for child in resp.child('player22').children:
|
for child in resp.child('player22').children:
|
||||||
if child.name == 'music':
|
if child.name == 'music':
|
||||||
songid = child.child_value('music_num')
|
songid = child.child_value('music_num')
|
||||||
@ -108,7 +109,27 @@ class PopnMusicLapistoriaClient(BaseClient):
|
|||||||
scores[songid] = [0, 0, 0, 0]
|
scores[songid] = [0, 0, 0, 0]
|
||||||
scores[songid][chart] = points
|
scores[songid][chart] = points
|
||||||
|
|
||||||
return {'medals': medals, 'scores': scores}
|
if child.name == "course":
|
||||||
|
courseid = child.child_value('course_id')
|
||||||
|
medal = child.child_value('clear_medal')
|
||||||
|
combo = child.child_value('max_cmbo')
|
||||||
|
stage1 = child.child_value('stage1_score')
|
||||||
|
stage2 = child.child_value('stage2_score')
|
||||||
|
stage3 = child.child_value('stage3_score')
|
||||||
|
stage4 = child.child_value('stage4_score')
|
||||||
|
total = child.child_value('total_score')
|
||||||
|
courses[courseid] = {
|
||||||
|
'id': courseid,
|
||||||
|
'medal': medal,
|
||||||
|
'combo': combo,
|
||||||
|
'stage1': stage1,
|
||||||
|
'stage2': stage2,
|
||||||
|
'stage3': stage3,
|
||||||
|
'stage4': stage4,
|
||||||
|
'total': total,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {'medals': medals, 'scores': scores, 'courses': courses}
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception(f'Unrecognized message type \'{msg_type}\'')
|
raise Exception(f'Unrecognized message type \'{msg_type}\'')
|
||||||
@ -164,6 +185,49 @@ class PopnMusicLapistoriaClient(BaseClient):
|
|||||||
resp = self.exchange('', call)
|
resp = self.exchange('', call)
|
||||||
self.assert_path(resp, "response/player22/@status")
|
self.assert_path(resp, "response/player22/@status")
|
||||||
|
|
||||||
|
def verify_player22_write_course(self, ref_id: str, course: Dict[str, int]) -> None:
|
||||||
|
call = self.call_node()
|
||||||
|
|
||||||
|
# Construct node
|
||||||
|
player22 = Node.void('player22')
|
||||||
|
call.add_child(player22)
|
||||||
|
player22.set_attribute('method', 'write_course')
|
||||||
|
player22.add_child(Node.s16('pref', 51))
|
||||||
|
player22.add_child(Node.string('location_id', 'JP-1'))
|
||||||
|
player22.add_child(Node.string('ref_id', ref_id))
|
||||||
|
player22.add_child(Node.string('data_id', ref_id))
|
||||||
|
player22.add_child(Node.string('name', self.NAME))
|
||||||
|
player22.add_child(Node.s16('chara_num', 1543))
|
||||||
|
player22.add_child(Node.s32('play_id', 0))
|
||||||
|
player22.add_child(Node.s16('course_id', course['id']))
|
||||||
|
player22.add_child(Node.s16('stage1_music_num', 148))
|
||||||
|
player22.add_child(Node.u8('stage1_sheet_num', 1))
|
||||||
|
player22.add_child(Node.s16('stage2_music_num', 550))
|
||||||
|
player22.add_child(Node.u8('stage2_sheet_num', 1))
|
||||||
|
player22.add_child(Node.s16('stage3_music_num', 1113))
|
||||||
|
player22.add_child(Node.u8('stage3_sheet_num', 1))
|
||||||
|
player22.add_child(Node.s16('stage4_music_num', 341))
|
||||||
|
player22.add_child(Node.u8('stage4_sheet_num', 1))
|
||||||
|
player22.add_child(Node.u8('norma_type', 2))
|
||||||
|
player22.add_child(Node.s32('norma_1_num', 5))
|
||||||
|
player22.add_child(Node.s32('norma_2_num', 0))
|
||||||
|
player22.add_child(Node.u8('clear_medal', course['medal']))
|
||||||
|
player22.add_child(Node.u8('clear_norma', 2))
|
||||||
|
player22.add_child(Node.s32('total_score', course['total']))
|
||||||
|
player22.add_child(Node.s16('max_combo', course['combo']))
|
||||||
|
|
||||||
|
for stage, music in enumerate([148, 550, 1113, 341]):
|
||||||
|
stagenode = Node.void('stage')
|
||||||
|
player22.add_child(stagenode)
|
||||||
|
stagenode.add_child(Node.u8('stage', stage))
|
||||||
|
stagenode.add_child(Node.s16('music_num', music))
|
||||||
|
stagenode.add_child(Node.u8('sheet_num', 1))
|
||||||
|
stagenode.add_child(Node.s32('score', course[f'stage{stage + 1}']))
|
||||||
|
|
||||||
|
# Swap with server
|
||||||
|
resp = self.exchange('', call)
|
||||||
|
self.assert_path(resp, "response/player22/@status")
|
||||||
|
|
||||||
def verify_player22_new(self, ref_id: str) -> None:
|
def verify_player22_new(self, ref_id: str) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
@ -250,6 +314,8 @@ class PopnMusicLapistoriaClient(BaseClient):
|
|||||||
for i in range(4):
|
for i in range(4):
|
||||||
if score[i] != 0:
|
if score[i] != 0:
|
||||||
raise Exception('Got nonzero scores count on a new card!')
|
raise Exception('Got nonzero scores count on a new card!')
|
||||||
|
for _ in scores['courses']:
|
||||||
|
raise Exception('Got nonzero courses count on a new card!')
|
||||||
|
|
||||||
for phase in [1, 2]:
|
for phase in [1, 2]:
|
||||||
if phase == 1:
|
if phase == 1:
|
||||||
@ -338,6 +404,29 @@ class PopnMusicLapistoriaClient(BaseClient):
|
|||||||
|
|
||||||
# Sleep so we don't end up putting in score history on the same second
|
# Sleep so we don't end up putting in score history on the same second
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Write a random course so we know we can retrieve them.
|
||||||
|
course = {
|
||||||
|
'id': random.randint(1, 100),
|
||||||
|
'medal': 2,
|
||||||
|
'combo': random.randint(10, 100),
|
||||||
|
'stage1': random.randint(70000, 100000),
|
||||||
|
'stage2': random.randint(70000, 100000),
|
||||||
|
'stage3': random.randint(70000, 100000),
|
||||||
|
'stage4': random.randint(70000, 100000),
|
||||||
|
}
|
||||||
|
course['total'] = sum(course[f'stage{i + 1}'] for i in range(4))
|
||||||
|
self.verify_player22_write_course(ref_id, course)
|
||||||
|
|
||||||
|
# Now, grab the profile one more time and see that it is there.
|
||||||
|
scores = self.verify_player22_read(ref_id, msg_type='query')
|
||||||
|
if len(scores['courses']) != 1:
|
||||||
|
raise Exception("Did not get a course back after saving!")
|
||||||
|
if course['id'] not in scores['courses']:
|
||||||
|
raise Exception("Did not get expected course back after saving!")
|
||||||
|
for key in ['medal', 'combo', 'stage1', 'stage2', 'stage3', 'stage4', 'total']:
|
||||||
|
if course[key] != scores['courses'][course['id']][key]:
|
||||||
|
raise Exception(f'Expected a {key} of \'{course[key]}\' but got \'{scores["courses"][course["id"]][key]}\'')
|
||||||
else:
|
else:
|
||||||
print("Skipping score checks for existing card")
|
print("Skipping score checks for existing card")
|
||||||
|
|
||||||
|
@ -240,6 +240,7 @@ class DBConstants:
|
|||||||
MUSECA_CLEAR_TYPE_CLEARED: Final[int] = 200
|
MUSECA_CLEAR_TYPE_CLEARED: Final[int] = 200
|
||||||
MUSECA_CLEAR_TYPE_FULL_COMBO: Final[int] = 300
|
MUSECA_CLEAR_TYPE_FULL_COMBO: Final[int] = 300
|
||||||
|
|
||||||
|
POPN_MUSIC_PLAY_MEDAL_NO_PLAY: Final[int] = 50
|
||||||
POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED: Final[int] = 100
|
POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED: Final[int] = 100
|
||||||
POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED: Final[int] = 200
|
POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED: Final[int] = 200
|
||||||
POPN_MUSIC_PLAY_MEDAL_STAR_FAILED: Final[int] = 300
|
POPN_MUSIC_PLAY_MEDAL_STAR_FAILED: Final[int] = 300
|
||||||
|
@ -633,9 +633,9 @@ class UserData(BaseData):
|
|||||||
|
|
||||||
return [UserID(result['userid']) for result in cursor.fetchall()]
|
return [UserID(result['userid']) for result in cursor.fetchall()]
|
||||||
|
|
||||||
def get_all_achievements(self, game: GameConstants, version: int) -> List[Tuple[UserID, Achievement]]:
|
def get_all_achievements(self, game: GameConstants, version: int, achievementid: Optional[int] = None, achievementtype: Optional[str] = None) -> List[Tuple[UserID, Achievement]]:
|
||||||
"""
|
"""
|
||||||
Given a game/version, find all achievements for al players.
|
Given a game/version, find all achievements for all players.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
game - Enum value identifier of the game looking up the user.
|
game - Enum value identifier of the game looking up the user.
|
||||||
@ -649,7 +649,14 @@ class UserData(BaseData):
|
|||||||
"refid.userid AS userid FROM achievement, refid WHERE refid.game = :game AND "
|
"refid.userid AS userid FROM achievement, refid WHERE refid.game = :game AND "
|
||||||
"refid.version = :version AND refid.refid = achievement.refid"
|
"refid.version = :version AND refid.refid = achievement.refid"
|
||||||
)
|
)
|
||||||
cursor = self.execute(sql, {'game': game.value, 'version': version})
|
params: Dict[str, Any] = {'game': game.value, 'version': version}
|
||||||
|
if achievementtype is not None:
|
||||||
|
sql += " AND achievement.type = :type"
|
||||||
|
params['type'] = achievementtype
|
||||||
|
if achievementid is not None:
|
||||||
|
sql += " AND achievement.id = :id"
|
||||||
|
params['id'] = achievementid
|
||||||
|
cursor = self.execute(sql, params)
|
||||||
|
|
||||||
achievements = []
|
achievements = []
|
||||||
for result in cursor.fetchall():
|
for result in cursor.fetchall():
|
||||||
|
@ -15,7 +15,7 @@ from bemani.frontend.types import g
|
|||||||
bishi_pages = Blueprint(
|
bishi_pages = Blueprint(
|
||||||
'bishi_pages',
|
'bishi_pages',
|
||||||
__name__,
|
__name__,
|
||||||
url_prefix='/bishi',
|
url_prefix=f'/{GameConstants.BISHI_BASHI.value}',
|
||||||
template_folder=templates_location,
|
template_folder=templates_location,
|
||||||
static_folder=static_location,
|
static_folder=static_location,
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,7 @@ from bemani.frontend.types import g
|
|||||||
ddr_pages = Blueprint(
|
ddr_pages = Blueprint(
|
||||||
'ddr_pages',
|
'ddr_pages',
|
||||||
__name__,
|
__name__,
|
||||||
url_prefix='/ddr',
|
url_prefix=f'/{GameConstants.DDR.value}',
|
||||||
template_folder=templates_location,
|
template_folder=templates_location,
|
||||||
static_folder=static_location,
|
static_folder=static_location,
|
||||||
)
|
)
|
||||||
|
@ -14,7 +14,7 @@ from bemani.frontend.types import g
|
|||||||
iidx_pages = Blueprint(
|
iidx_pages = Blueprint(
|
||||||
'iidx_pages',
|
'iidx_pages',
|
||||||
__name__,
|
__name__,
|
||||||
url_prefix='/iidx',
|
url_prefix=f'/{GameConstants.IIDX.value}',
|
||||||
template_folder=templates_location,
|
template_folder=templates_location,
|
||||||
static_folder=static_location,
|
static_folder=static_location,
|
||||||
)
|
)
|
||||||
|
@ -14,7 +14,7 @@ from bemani.frontend.types import g
|
|||||||
jubeat_pages = Blueprint(
|
jubeat_pages = Blueprint(
|
||||||
'jubeat_pages',
|
'jubeat_pages',
|
||||||
__name__,
|
__name__,
|
||||||
url_prefix='/jubeat',
|
url_prefix=f'/{GameConstants.JUBEAT.value}',
|
||||||
template_folder=templates_location,
|
template_folder=templates_location,
|
||||||
static_folder=static_location,
|
static_folder=static_location,
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,7 @@ from bemani.frontend.types import g
|
|||||||
museca_pages = Blueprint(
|
museca_pages = Blueprint(
|
||||||
'museca_pages',
|
'museca_pages',
|
||||||
__name__,
|
__name__,
|
||||||
url_prefix='/museca',
|
url_prefix=f'/{GameConstants.MUSECA.value}',
|
||||||
template_folder=templates_location,
|
template_folder=templates_location,
|
||||||
static_folder=static_location,
|
static_folder=static_location,
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,7 @@ from bemani.frontend.types import g
|
|||||||
popn_pages = Blueprint(
|
popn_pages = Blueprint(
|
||||||
'popn_pages',
|
'popn_pages',
|
||||||
__name__,
|
__name__,
|
||||||
url_prefix='/pnm',
|
url_prefix=f'/{GameConstants.POPN_MUSIC.value}',
|
||||||
template_folder=templates_location,
|
template_folder=templates_location,
|
||||||
static_folder=static_location,
|
static_folder=static_location,
|
||||||
)
|
)
|
||||||
|
@ -26,7 +26,7 @@ class PopnMusicFrontend(FrontendBase):
|
|||||||
VersionConstants.POPN_MUSIC_TUNE_STREET: 0,
|
VersionConstants.POPN_MUSIC_TUNE_STREET: 0,
|
||||||
VersionConstants.POPN_MUSIC_FANTASIA: 2,
|
VersionConstants.POPN_MUSIC_FANTASIA: 2,
|
||||||
VersionConstants.POPN_MUSIC_SUNNY_PARK: 2,
|
VersionConstants.POPN_MUSIC_SUNNY_PARK: 2,
|
||||||
VersionConstants.POPN_MUSIC_LAPISTORIA: 4,
|
VersionConstants.POPN_MUSIC_LAPISTORIA: 2,
|
||||||
VersionConstants.POPN_MUSIC_ECLALE: 4,
|
VersionConstants.POPN_MUSIC_ECLALE: 4,
|
||||||
VersionConstants.POPN_MUSIC_USANEKO: 4,
|
VersionConstants.POPN_MUSIC_USANEKO: 4,
|
||||||
}
|
}
|
||||||
@ -40,6 +40,7 @@ class PopnMusicFrontend(FrontendBase):
|
|||||||
formatted_score['combo'] = score.data.get_int('combo', -1)
|
formatted_score['combo'] = score.data.get_int('combo', -1)
|
||||||
formatted_score['medal'] = score.data.get_int('medal')
|
formatted_score['medal'] = score.data.get_int('medal')
|
||||||
formatted_score['status'] = {
|
formatted_score['status'] = {
|
||||||
|
PopnMusicBase.PLAY_MEDAL_NO_PLAY: "No Play",
|
||||||
PopnMusicBase.PLAY_MEDAL_CIRCLE_FAILED: "○ Failed",
|
PopnMusicBase.PLAY_MEDAL_CIRCLE_FAILED: "○ Failed",
|
||||||
PopnMusicBase.PLAY_MEDAL_DIAMOND_FAILED: "◇ Failed",
|
PopnMusicBase.PLAY_MEDAL_DIAMOND_FAILED: "◇ Failed",
|
||||||
PopnMusicBase.PLAY_MEDAL_STAR_FAILED: "☆ Failed",
|
PopnMusicBase.PLAY_MEDAL_STAR_FAILED: "☆ Failed",
|
||||||
|
@ -15,7 +15,7 @@ from bemani.frontend.types import g
|
|||||||
reflec_pages = Blueprint(
|
reflec_pages = Blueprint(
|
||||||
'reflec_pages',
|
'reflec_pages',
|
||||||
__name__,
|
__name__,
|
||||||
url_prefix='/reflec',
|
url_prefix=f'/{GameConstants.REFLEC_BEAT.value}',
|
||||||
template_folder=templates_location,
|
template_folder=templates_location,
|
||||||
static_folder=static_location,
|
static_folder=static_location,
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,7 @@ from bemani.frontend.types import g
|
|||||||
sdvx_pages = Blueprint(
|
sdvx_pages = Blueprint(
|
||||||
'sdvx_pages',
|
'sdvx_pages',
|
||||||
__name__,
|
__name__,
|
||||||
url_prefix='/sdvx',
|
url_prefix=f'/{GameConstants.SDVX.value}',
|
||||||
template_folder=templates_location,
|
template_folder=templates_location,
|
||||||
static_folder=static_location,
|
static_folder=static_location,
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user