From 8af63c2013d86d6e557a19f7abb28e7a8282d6bd Mon Sep 17 00:00:00 2001 From: Jennifer Taylor Date: Mon, 29 Sep 2025 02:19:50 +0000 Subject: [PATCH] Add playtracking chart, store attempts there since we don't have a real chart. Juggle some stuff around for upcoming frontend. --- bemani/backend/danevo/base.py | 98 +++++++++++++++++++- bemani/backend/danevo/danevo.py | 159 +++++++++++++------------------- bemani/utils/read.py | 2 +- 3 files changed, 160 insertions(+), 99 deletions(-) diff --git a/bemani/backend/danevo/base.py b/bemani/backend/danevo/base.py index 389d6d4..6de8926 100644 --- a/bemani/backend/danevo/base.py +++ b/bemani/backend/danevo/base.py @@ -1,11 +1,11 @@ # vim: set fileencoding=utf-8 from typing import Optional +from typing_extensions import Final from bemani.backend.base import Base from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler -from bemani.common import ( - GameConstants, -) +from bemani.common import GameConstants, ValidatedDict +from bemani.data import UserID class DanceEvolutionBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): @@ -15,9 +15,101 @@ class DanceEvolutionBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): game: GameConstants = GameConstants.DANCE_EVOLUTION + CHART_TYPE_LIGHT: Final[int] = 0 + CHART_TYPE_STANDARD: Final[int] = 1 + CHART_TYPE_EXTREME: Final[int] = 2 + CHART_TYPE_STEALTH: Final[int] = 3 + CHART_TYPE_MASTER: Final[int] = 4 + CHART_TYPE_PLAYTRACKING: Final[int] = 5 + + GRADE_FAILED: Final[int] = 100 + GRADE_E: Final[int] = 200 + GRADE_D: Final[int] = 300 + GRADE_C: Final[int] = 400 + GRADE_B: Final[int] = 500 + GRADE_A: Final[int] = 600 + GRADE_AA: Final[int] = 700 + GRADE_AAA: Final[int] = 800 + def previous_version(self) -> Optional["DanceEvolutionBase"]: """ Returns the previous version of the game, based on this game. Should be overridden. """ return None + + def update_score( + self, + userid: UserID, + songid: int, + chart: int, + points: int, + grade: int, + combo: int, + full_combo: bool, + ) -> None: + """ + Given various pieces of a score, update the user's high score. + """ + if chart not in { + self.CHART_TYPE_LIGHT, + self.CHART_TYPE_STANDARD, + self.CHART_TYPE_EXTREME, + self.CHART_TYPE_STEALTH, + self.CHART_TYPE_MASTER, + }: + raise Exception(f"Invalid chart {chart}") + if grade not in { + self.GRADE_FAILED, + self.GRADE_E, + self.GRADE_D, + self.GRADE_C, + self.GRADE_B, + self.GRADE_A, + self.GRADE_AA, + self.GRADE_AAA, + }: + raise Exception(f"Invalid grade {grade}") + + oldscore = self.data.local.music.get_score( + self.game, + self.version, + userid, + songid, + chart, + ) + + if oldscore is None: + # If it is a new score, create a new dictionary to add to + scoredata = ValidatedDict({}) + highscore = True + else: + # Set the score to any new record achieved + highscore = points >= oldscore.points + points = max(oldscore.points, points) + scoredata = oldscore.data + + # Save combo + scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo)) + + # Save grade + scoredata.replace_int("grade", max(scoredata.get_int("grade"), grade)) + + # Save full combo indicator. + scoredata.replace_bool("full_combo", scoredata.get_bool("full_combo") or full_combo) + + # Look up where this score was earned + lid = self.get_machine_id() + + # Write the new score back + self.data.local.music.put_score( + self.game, + self.version, + userid, + songid, + chart, + lid, + points, + scoredata, + highscore, + ) diff --git a/bemani/backend/danevo/danevo.py b/bemani/backend/danevo/danevo.py index a5ec656..b8db6bb 100644 --- a/bemani/backend/danevo/danevo.py +++ b/bemani/backend/danevo/danevo.py @@ -5,8 +5,8 @@ from typing_extensions import Final from bemani.backend.ess import EventLogHandler from bemani.backend.danevo.base import DanceEvolutionBase -from bemani.common import ValidatedDict, VersionConstants, Profile, CardCipher, Time -from bemani.data import UserID +from bemani.common import VersionConstants, Profile, CardCipher, Time +from bemani.data import ScoreSaveException from bemani.protocol import Node @@ -84,12 +84,6 @@ class DanceEvolution( DATA04_TOTAL_SCORE_EARNED_OFFSET: Final[int] = 9 - CHART_LIGHT: Final[int] = 0 - CHART_STANDARD: Final[int] = 1 - CHART_EXTREME: Final[int] = 2 - CHART_STEALTH: Final[int] = 3 - CHART_MASTER: Final[int] = 4 - GAME_GRADE_FAILED: Final[int] = 0 GAME_GRADE_E: Final[int] = 1 GAME_GRADE_D: Final[int] = 2 @@ -125,82 +119,6 @@ class DanceEvolution( return self.update_machine_name(shopname) - def update_score( - self, - userid: UserID, - songid: int, - chart: int, - points: int, - grade: int, - combo: int, - full_combo: bool, - ) -> None: - """ - Given various pieces of a score, update the user's high score. - """ - if chart not in { - self.CHART_LIGHT, - self.CHART_STANDARD, - self.CHART_EXTREME, - self.CHART_STEALTH, - self.CHART_MASTER, - }: - raise Exception(f"Invalid chart {chart}") - if grade not in { - self.GAME_GRADE_FAILED, - self.GAME_GRADE_E, - self.GAME_GRADE_D, - self.GAME_GRADE_C, - self.GAME_GRADE_B, - self.GAME_GRADE_A, - self.GAME_GRADE_AA, - self.GAME_GRADE_AAA, - }: - raise Exception(f"Invalid grade {grade}") - - oldscore = self.data.local.music.get_score( - self.game, - self.version, - userid, - songid, - chart, - ) - - if oldscore is None: - # If it is a new score, create a new dictionary to add to - scoredata = ValidatedDict({}) - highscore = True - else: - # Set the score to any new record achieved - highscore = points >= oldscore.points - points = max(oldscore.points, points) - scoredata = oldscore.data - - # Save combo - scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo)) - - # Save grade - scoredata.replace_int("grade", max(scoredata.get_int("grade"), grade)) - - # Save full combo indicator. - scoredata.replace_bool("full_combo", scoredata.get_bool("full_combo") or full_combo) - - # Look up where this score was earned - lid = self.get_machine_id() - - # Write the new score back - self.data.local.music.put_score( - self.game, - self.version, - userid, - songid, - chart, - lid, - points, - scoredata, - highscore, - ) - def handle_tax_get_phase_request(self, request: Node) -> Node: tax = Node.void("tax") tax.add_child(Node.s32("phase", 0)) @@ -389,23 +307,64 @@ class DanceEvolution( # Game might be set to 1 song. continue + # For the purpose of popularity tracking, save an attempt for this song into the virtual + # attempt chart, since we can't know for certain what chart an attempt was associated with. + now = Time.now() + lid = self.get_machine_id() + + for bump in range(10): + timestamp = now + bump + + self.data.local.music.put_score( + self.game, + self.version, + userid, + played, + self.CHART_TYPE_PLAYTRACKING, + lid, + scored, + {}, + False, + timestamp=timestamp, + ) + + try: + self.data.local.music.put_attempt( + self.game, + self.version, + userid, + played, + self.CHART_TYPE_PLAYTRACKING, + lid, + scored, + {}, + False, + timestamp=timestamp, + ) + except ScoreSaveException: + # Try again one second in the future + continue + + # We saved successfully + break + # First, calculate whether we're going to look at DATA01-05 or DATA11-15. if played < 63: mapping = { - "DATA01": self.CHART_LIGHT, - "DATA02": self.CHART_STANDARD, - "DATA03": self.CHART_EXTREME, - "DATA04": self.CHART_STEALTH, - "DATA05": self.CHART_MASTER, + "DATA01": self.CHART_TYPE_LIGHT, + "DATA02": self.CHART_TYPE_STANDARD, + "DATA03": self.CHART_TYPE_EXTREME, + "DATA04": self.CHART_TYPE_STEALTH, + "DATA05": self.CHART_TYPE_MASTER, } offset = played * 8 else: mapping = { - "DATA11": self.CHART_LIGHT, - "DATA12": self.CHART_STANDARD, - "DATA13": self.CHART_EXTREME, - "DATA14": self.CHART_STEALTH, - "DATA15": self.CHART_MASTER, + "DATA11": self.CHART_TYPE_LIGHT, + "DATA12": self.CHART_TYPE_STANDARD, + "DATA13": self.CHART_TYPE_EXTREME, + "DATA14": self.CHART_TYPE_STEALTH, + "DATA15": self.CHART_TYPE_MASTER, } offset = (played - 63) * 8 @@ -432,8 +391,18 @@ class DanceEvolution( # Found it! full_combo = bool(stats & 0x10) letter_grade = (stats >> 1) & 0x7 + grade = { + self.GAME_GRADE_FAILED: self.GRADE_FAILED, + self.GAME_GRADE_E: self.GRADE_E, + self.GAME_GRADE_D: self.GRADE_D, + self.GAME_GRADE_C: self.GRADE_C, + self.GAME_GRADE_B: self.GRADE_B, + self.GAME_GRADE_A: self.GRADE_A, + self.GAME_GRADE_AA: self.GRADE_AA, + self.GAME_GRADE_AAA: self.GRADE_AAA, + }[letter_grade] - self.update_score(userid, played, mapping[key], scored, letter_grade, combo, full_combo) + self.update_score(userid, played, mapping[key], scored, grade, combo, full_combo) playerdata.add_child(Node.s32("result", 0)) return playerdata diff --git a/bemani/utils/read.py b/bemani/utils/read.py index daa4d72..1fac9ac 100644 --- a/bemani/utils/read.py +++ b/bemani/utils/read.py @@ -6172,7 +6172,7 @@ class ImportDanceEvolution(ImportBase): # Import it self.start_batch() - for chart_id in [0, 1, 2, 3, 4]: + for chart_id in [0, 1, 2, 3, 4, 5]: # First, try to find in the DB from another version old_id = self.get_music_id_for_song(song["id"], chart_id) if self.no_combine or old_id is None: