From 2f6e116405501bb23a4d6d8e4d1aba630bf82b4b Mon Sep 17 00:00:00 2001 From: Jennifer Taylor Date: Tue, 7 Sep 2021 17:56:15 +0000 Subject: [PATCH] Types, including Final, for all backend classes. Also fixed old-style routing for eacoin. --- bemani/backend/bishi/base.py | 2 +- bemani/backend/bishi/bishi.py | 7 +- bemani/backend/bishi/factory.py | 4 +- bemani/backend/core/eacoin.py | 815 ++++++++++++----------- bemani/backend/ddr/base.py | 65 +- bemani/backend/ddr/ddr2013.py | 58 +- bemani/backend/ddr/ddr2014.py | 58 +- bemani/backend/ddr/ddra20.py | 4 +- bemani/backend/ddr/ddrace.py | 151 ++--- bemani/backend/ddr/ddrx2.py | 56 +- bemani/backend/ddr/ddrx3.py | 60 +- bemani/backend/ddr/factory.py | 4 +- bemani/backend/ddr/stubs.py | 44 +- bemani/backend/iidx/base.py | 101 +-- bemani/backend/iidx/bistrover.py | 6 +- bemani/backend/iidx/cannonballers.py | 128 ++-- bemani/backend/iidx/copula.py | 128 ++-- bemani/backend/iidx/course.py | 7 +- bemani/backend/iidx/factory.py | 4 +- bemani/backend/iidx/heroicverse.py | 6 +- bemani/backend/iidx/pendual.py | 122 ++-- bemani/backend/iidx/rootage.py | 128 ++-- bemani/backend/iidx/sinobuz.py | 128 ++-- bemani/backend/iidx/spada.py | 122 ++-- bemani/backend/iidx/stubs.py | 76 +-- bemani/backend/iidx/tricoro.py | 122 ++-- bemani/backend/jubeat/base.py | 37 +- bemani/backend/jubeat/clan.py | 50 +- bemani/backend/jubeat/course.py | 15 +- bemani/backend/jubeat/factory.py | 4 +- bemani/backend/jubeat/festo.py | 4 +- bemani/backend/jubeat/prop.py | 28 +- bemani/backend/jubeat/qubell.py | 16 +- bemani/backend/jubeat/saucer.py | 7 +- bemani/backend/jubeat/saucerfulfill.py | 22 +- bemani/backend/jubeat/stubs.py | 28 +- bemani/backend/mga/base.py | 2 +- bemani/backend/mga/factory.py | 4 +- bemani/backend/mga/mga.py | 7 +- bemani/backend/museca/base.py | 33 +- bemani/backend/museca/common.py | 1 - bemani/backend/museca/museca1.py | 42 +- bemani/backend/museca/museca1plus.py | 44 +- bemani/backend/popn/base.py | 43 +- bemani/backend/popn/common.py | 7 +- bemani/backend/popn/eclale.py | 40 +- bemani/backend/popn/factory.py | 4 +- bemani/backend/popn/fantasia.py | 46 +- bemani/backend/popn/lapistoria.py | 42 +- bemani/backend/popn/peace.py | 6 +- bemani/backend/popn/stubs.py | 72 +- bemani/backend/popn/sunnypark.py | 46 +- bemani/backend/popn/tunestreet.py | 66 +- bemani/backend/popn/usaneko.py | 6 +- bemani/backend/reflec/base.py | 29 +- bemani/backend/reflec/colette.py | 18 +- bemani/backend/reflec/groovin.py | 28 +- bemani/backend/reflec/limelight.py | 16 +- bemani/backend/reflec/reflecbeat.py | 14 +- bemani/backend/reflec/volzza.py | 7 +- bemani/backend/reflec/volzza2.py | 7 +- bemani/backend/reflec/volzzabase.py | 21 +- bemani/backend/sdvx/base.py | 47 +- bemani/backend/sdvx/booth.py | 38 +- bemani/backend/sdvx/factory.py | 4 +- bemani/backend/sdvx/gravitywars.py | 47 +- bemani/backend/sdvx/gravitywars_s1.py | 3 +- bemani/backend/sdvx/gravitywars_s2.py | 3 +- bemani/backend/sdvx/heavenlyhaven.py | 104 +-- bemani/backend/sdvx/infiniteinfection.py | 50 +- bemani/common/validateddict.py | 19 + 71 files changed, 1803 insertions(+), 1780 deletions(-) diff --git a/bemani/backend/bishi/base.py b/bemani/backend/bishi/base.py index d7ace86..e0de271 100644 --- a/bemani/backend/bishi/base.py +++ b/bemani/backend/bishi/base.py @@ -13,7 +13,7 @@ class BishiBashiBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): happened. """ - game = GameConstants.BISHI_BASHI + game: GameConstants = GameConstants.BISHI_BASHI def previous_version(self) -> Optional['BishiBashiBase']: """ diff --git a/bemani/backend/bishi/bishi.py b/bemani/backend/bishi/bishi.py index 3feb528..d04ed6c 100644 --- a/bemani/backend/bishi/bishi.py +++ b/bemani/backend/bishi/bishi.py @@ -1,6 +1,5 @@ # vim: set fileencoding=utf-8 import binascii -import copy import base64 from collections import Iterable from typing import Any, Dict, List, Sequence, Union @@ -17,8 +16,8 @@ class TheStarBishiBashi( BishiBashiBase, ): - name = "The★BishiBashi" - version = VersionConstants.BISHI_BASHI_TSBB + name: str = "The★BishiBashi" + version: int = VersionConstants.BISHI_BASHI_TSBB @classmethod def get_settings(cls) -> Dict[str, Any]: @@ -324,7 +323,7 @@ class TheStarBishiBashi( def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile, is_new: bool) -> Profile: # Profile save request, data values are base64 encoded. # d is a CSV, and bin1 is binary data. - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() strdatas: List[bytes] = [] bindatas: List[bytes] = [] diff --git a/bemani/backend/bishi/factory.py b/bemani/backend/bishi/factory.py index 90e094e..6f85807 100644 --- a/bemani/backend/bishi/factory.py +++ b/bemani/backend/bishi/factory.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import List, Optional, Type from bemani.backend.base import Base, Factory from bemani.backend.bishi.bishi import TheStarBishiBashi @@ -8,7 +8,7 @@ from bemani.data import Config, Data class BishiBashiFactory(Factory): - MANAGED_CLASSES = [ + MANAGED_CLASSES: List[Type[Base]] = [ TheStarBishiBashi, ] diff --git a/bemani/backend/core/eacoin.py b/bemani/backend/core/eacoin.py index 92058c1..084902e 100644 --- a/bemani/backend/core/eacoin.py +++ b/bemani/backend/core/eacoin.py @@ -9,6 +9,10 @@ from bemani.common import Time, CardCipher class PASELIHandler(Base): """ A mixin that can be used to provide PASELI services to a game. + + Handle PASELI requests. The game will check out a session at the beginning + of the game, make PASELI purchases against that session, and then close it + at the end of of a game. This handler ensures that this works for all games. """ INFINITE_PASELI_AMOUNT: Final[int] = 57300 @@ -22,12 +26,9 @@ class PASELIHandler(Base): def handle_eacoin_request(self, request: Node) -> Optional[Node]: """ - Handle PASELI requests. The game will check out a session at the beginning - of the game, make PASELI purchases against that session, and then close it - ad the end of of a game. This handler ensures that this works for all games. + First, a generic handler to catch if paseli is not enabled, so that each + individual method below doesn't need to handle it. """ - method = request.attribute('method') - if not self.config.paseli.enabled: # Refuse to respond, we don't have PASELI enabled print("PASELI not enabled, ignoring eacoin request") @@ -35,406 +36,406 @@ class PASELIHandler(Base): root.set_attribute('status', str(Status.NOT_ALLOWED)) return root - if method == 'checkin': - root = Node.void('eacoin') - cardid = request.child_value('cardid') - pin = request.child_value('passwd') - - if cardid is None or pin is None: - # Refuse to return anything - print("Invalid eacoin checkin request, missing cardid or pin") - root.set_attribute('status', str(Status.NO_PROFILE)) - return root - - userid = self.data.local.user.from_cardid(cardid) - if userid is None: - # Refuse to do anything - print("No user for eacoin checkin request") - root.set_attribute('status', str(Status.NO_PROFILE)) - return root - - valid = self.data.local.user.validate_pin(userid, pin) - if not valid: - # Refuse to do anything - print("User entered invalid pin for eacoin checkin request") - root.set_attribute('status', str(Status.INVALID_PIN)) - return root - - session = self.data.local.user.create_session(userid) - - if self.config.paseli.infinite: - balance = PASELIHandler.INFINITE_PASELI_AMOUNT - else: - if self.config.machine.arcade is None: - # There's no arcade for this machine, but infinite is not - # enabled, so there's no way to find a balance. - balance = 0 - else: - balance = self.data.local.user.get_balance(userid, self.config.machine.arcade) - - root.add_child(Node.s16('sequence', 0)) - root.add_child(Node.u8('acstatus', 0)) - root.add_child(Node.string('acid', 'DUMMY_ID')) - root.add_child(Node.string('acname', 'DUMMY_NAME')) - root.add_child(Node.s32('balance', balance)) - root.add_child(Node.string('sessid', session)) - return root - - if method == 'opcheckin': - root = Node.void('eacoin') - passwd = request.child_value('passwd') - - if passwd is None: - # Refuse to return anything - print("Invalid eacoin checkin request, missing passwd") - root.set_attribute('status', str(Status.NO_PROFILE)) - return root - - if self.config.machine.arcade is None: - # Machine doesn't belong to an arcade - print("Machine doesn't belong to an arcade") - root.set_attribute('status', str(Status.NO_PROFILE)) - return root - - arcade = self.data.local.machine.get_arcade(self.config.machine.arcade) - if arcade is None: - # Refuse to do anything - print("No arcade for operator checkin request") - root.set_attribute('status', str(Status.NO_PROFILE)) - return root - - if arcade.pin != passwd: - # Refuse to do anything - print("User entered invalid pin for operator checkin request") - root.set_attribute('status', str(Status.INVALID_PIN)) - return root - - session = self.data.local.machine.create_session(arcade.id) - root.add_child(Node.string('sessid', session)) - return root - - elif method == 'consume': - - def make_resp(status: int, balance: int) -> Node: - root = Node.void('eacoin') - root.add_child(Node.u8('acstatus', status)) - root.add_child(Node.u8('autocharge', 0)) - root.add_child(Node.s32('balance', balance)) - return root - - session = request.child_value('sessid') - payment = request.child_value('payment') - service = request.child_value('service') - details = request.child_value('detail') - if session is None or payment is None: - # Refuse to do anything - print("Invalid eacoin consume request, missing sessid or payment") - return make_resp(2, 0) - - userid = self.data.local.user.from_session(session) - if userid is None: - # Refuse to do anything - print("Invalid session for eacoin consume request") - return make_resp(2, 0) - - if self.config.paseli.infinite: - balance = PASELIHandler.INFINITE_PASELI_AMOUNT - payment - else: - if self.config.machine.arcade is None: - # There's no arcade for this machine, but infinite is not - # enabled, so there's no way to find a balance, assume failed - # consume payment. - balance = None - else: - # Look up the new balance based on this delta. If there isn't enough, - # we will end up returning None here and exit without performing. - balance = self.data.local.user.update_balance(userid, self.config.machine.arcade, -payment) - - if balance is None: - print("Not enough balance for eacoin consume request") - return make_resp(1, self.data.local.user.get_balance(userid, self.config.machine.arcade)) - else: - self.data.local.network.put_event( - 'paseli_transaction', - { - 'delta': -payment, - 'balance': balance, - 'service': -service, - 'reason': details, - 'pcbid': self.config.machine.pcbid, - }, - userid=userid, - arcadeid=self.config.machine.arcade, - ) - - return make_resp(0, balance) - - elif method == 'getlog': - root = Node.void('eacoin') - sessid = request.child_value('sessid') - logtype = request.child_value('logtype') - target = request.child_value('target') - limit = request.child_value('perpage') - offset = request.child_value('offset') - - # Try to determine whether its a user or an arcade session - userid = self.data.local.user.from_session(sessid) - if userid is None: - arcadeid = self.data.local.machine.from_session(sessid) - else: - arcadeid = None - - # Bail out if we don't have any idea what session this is - if userid is None and arcadeid is None: - print("Unable to determine session type") - return root - - # If we're a user session, also look up the current arcade - # so we display only entries that happened on this arcade. - if userid is not None: - arcade = self.data.local.machine.get_arcade(self.config.machine.arcade) - if arcade is None: - print("Machine doesn't belong to an arcade") - return root - arcadeid = arcade.id - - # Now, look up all transactions for this specific group - events = self.data.local.network.get_events( - userid=userid, - arcadeid=arcadeid, - event='paseli_transaction', - ) - - # Further filter it down to the current PCBID - events = [event for event in events if event.data.get('pcbid') == target] - - # Grab the end of day today as a timestamp - end_of_today = Time.end_of_today() - time_format = '%Y-%m-%d %H:%M:%S' - date_format = '%Y-%m-%d' - - # Set up common structure - lognode = Node.void(logtype) - topic = Node.void('topic') - lognode.add_child(topic) - summary = Node.void('summary') - lognode.add_child(summary) - - # Display what day we are summed to - topic.add_child(Node.string('sumdate', Time.format(Time.now(), date_format))) - - if logtype == 'last7days': - # We show today in the today total, last 7 days prior in the week total - beginning_of_today = end_of_today - Time.SECONDS_IN_DAY - end_of_week = beginning_of_today - beginning_of_week = end_of_week - Time.SECONDS_IN_WEEK - - topic.add_child(Node.string('sumfrom', Time.format(beginning_of_week, date_format))) - topic.add_child(Node.string('sumto', Time.format(end_of_week, date_format))) - today_total = sum([ - -event.data.get_int('delta') for event in events - if event.timestamp >= beginning_of_today and event.timestamp < end_of_today - ]) - - today_total = sum([ - -event.data.get_int('delta') for event in events - if event.timestamp >= beginning_of_today and event.timestamp < end_of_today - ]) - week_txns = [ - -event.data.get_int('delta') for event in events - if event.timestamp >= beginning_of_week and event.timestamp < end_of_week - ] - week_total = sum(week_txns) - if len(week_txns) > 0: - week_avg = int(sum(week_txns) / len(week_txns)) - else: - week_avg = 0 - - # We display the totals for each day starting with yesterday and up through 7 days prior. - # Index starts at 0 = yesterday, 1 = the day before, etc... - items = [] - for days in range(0, 7): - end_of_day = end_of_week - (days * Time.SECONDS_IN_DAY) - start_of_day = end_of_day - Time.SECONDS_IN_DAY - - items.append(sum([ - -event.data.get_int('delta') for event in events - if event.timestamp >= start_of_day and event.timestamp < end_of_day - ])) - - topic.add_child(Node.s32('today', today_total)) - topic.add_child(Node.s32('average', week_avg)) - topic.add_child(Node.s32('total', week_total)) - summary.add_child(Node.s32_array('items', items)) - - if logtype == 'last52weeks': - # Start one week back, since the operator can look at last7days for newer stuff. - beginning_of_today = end_of_today - Time.SECONDS_IN_DAY - end_of_52_weeks = beginning_of_today - Time.SECONDS_IN_WEEK - - topic.add_child(Node.string('sumfrom', Time.format(end_of_52_weeks - (52 * Time.SECONDS_IN_WEEK), date_format))) - topic.add_child(Node.string('sumto', Time.format(end_of_52_weeks, date_format))) - - # We index backwards, where index 0 = the first week back, 1 = the next week back after that, etc... - items = [] - for weeks in range(0, 52): - end_of_range = end_of_52_weeks - (weeks * Time.SECONDS_IN_WEEK) - beginning_of_range = end_of_range - Time.SECONDS_IN_WEEK - - items.append(sum([ - -event.data.get_int('delta') for event in events - if event.timestamp >= beginning_of_range and event.timestamp < end_of_range - ])) - - summary.add_child(Node.s32_array('items', items)) - - if logtype == 'eachday': - start_ts = Time.now() - end_ts = Time.now() - weekdays = [0] * 7 - - for event in events: - event_day = Time.days_into_week(event.timestamp) - weekdays[event_day] = weekdays[event_day] - event.data.get_int('delta') - if event.timestamp < start_ts: - start_ts = event.timestamp - - topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format))) - topic.add_child(Node.string('sumto', Time.format(end_ts, date_format))) - summary.add_child(Node.s32_array('items', weekdays)) - - if logtype == 'eachhour': - start_ts = Time.now() - end_ts = Time.now() - hours = [0] * 24 - - for event in events: - event_hour = int((event.timestamp % Time.SECONDS_IN_DAY) / Time.SECONDS_IN_HOUR) - hours[event_hour] = hours[event_hour] - event.data.get_int('delta') - if event.timestamp < start_ts: - start_ts = event.timestamp - - topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format))) - topic.add_child(Node.string('sumto', Time.format(end_ts, date_format))) - summary.add_child(Node.s32_array('items', hours)) - - if logtype == 'detail': - history = Node.void('history') - lognode.add_child(history) - - # Respect details paging - if offset is not None: - events = events[offset:] - if limit is not None: - events = events[:limit] - - # Output the details themselves - for event in events: - card_no = '' - if event.userid is not None: - user = self.data.local.user.get_user(event.userid) - if user is not None: - cards = self.data.local.user.get_cards(user.id) - if len(cards) > 0: - card_no = CardCipher.encode(cards[0]) - - item = Node.void('item') - history.add_child(item) - item.add_child(Node.string('date', Time.format(event.timestamp, time_format))) - item.add_child(Node.s32('consume', -event.data.get_int('delta'))) - item.add_child(Node.s32('service', -event.data.get_int('service'))) - item.add_child(Node.string('cardtype', '')) - item.add_child(Node.string('cardno', ' ' * self.paseli_padding + card_no)) - item.add_child(Node.string('title', '')) - item.add_child(Node.string('systemid', '')) - - if logtype == 'lastmonths': - year, month, _ = Time.todays_date() - this_month = Time.timestamp_from_date(year, month) - last_month = Time.timestamp_from_date(year, month - 1) - month_before = Time.timestamp_from_date(year, month - 2) - - topic.add_child(Node.string('sumfrom', Time.format(month_before, date_format))) - topic.add_child(Node.string('sumto', Time.format(this_month, date_format))) - - for (start, end) in [(month_before, last_month), (last_month, this_month)]: - year, month, _ = Time.date_from_timestamp(start) - - items = [] - for day in range(0, 31): - begin_ts = start + (day * Time.SECONDS_IN_DAY) - end_ts = begin_ts + Time.SECONDS_IN_DAY - if begin_ts >= end: - # Passed the end of this month - items.append(0) - else: - # Sum up all the txns for this day - items.append(sum([ - -event.data.get_int('delta') for event in events - if event.timestamp >= begin_ts and event.timestamp < end_ts - ])) - - item = Node.void('item') - summary.add_child(item) - item.add_child(Node.s32('year', year)) - item.add_child(Node.s32('month', month)) - item.add_child(Node.s32_array('items', items)) - - root.add_child(Node.u8('processing', 0)) - root.add_child(lognode) - return root - - elif method == 'opchpass': - root = Node.void('eacoin') - oldpass = request.child_value('passwd') - newpass = request.child_value('newpasswd') - - if oldpass is None or newpass is None: - # Refuse to return anything - print("Invalid eacoin pass change request, missing passwd") - root.set_attribute('status', str(Status.NO_PROFILE)) - return root - - if self.config.machine.arcade is None: - # Machine doesn't belong to an arcade - print("Machine doesn't belong to an arcade") - root.set_attribute('status', str(Status.NO_PROFILE)) - return root - - arcade = self.data.local.machine.get_arcade(self.config.machine.arcade) - if arcade is None: - # Refuse to do anything - print("No arcade for operator pass change request") - root.set_attribute('status', str(Status.NO_PROFILE)) - return root - - if arcade.pin != oldpass: - # Refuse to do anything - print("User entered invalid pin for operator pass change request") - root.set_attribute('status', str(Status.INVALID_PIN)) - return root - - arcade.pin = newpass - self.data.local.machine.put_arcade(arcade) - return root - - elif method == 'checkout': - session = request.child_value('sessid') - if session is not None: - # Destroy the session so it can't be used for any other purchases - self.data.local.user.destroy_session(session) - - root = Node.void('eacoin') - return root - - elif method == 'opcheckout': - session = request.child_value('sessid') - if session is not None: - # Destroy the session so it can't be used for any other purchases - self.data.local.machine.destroy_session(session) - - root = Node.void('eacoin') - return root - - # Invalid method + # This is fine, let the individual request handlers handle this packet. return None + + def handle_eacoin_checkin_request(self, request: Node) -> Node: + root = Node.void('eacoin') + cardid = request.child_value('cardid') + pin = request.child_value('passwd') + + if cardid is None or pin is None: + # Refuse to return anything + print("Invalid eacoin checkin request, missing cardid or pin") + root.set_attribute('status', str(Status.NO_PROFILE)) + return root + + userid = self.data.local.user.from_cardid(cardid) + if userid is None: + # Refuse to do anything + print("No user for eacoin checkin request") + root.set_attribute('status', str(Status.NO_PROFILE)) + return root + + valid = self.data.local.user.validate_pin(userid, pin) + if not valid: + # Refuse to do anything + print("User entered invalid pin for eacoin checkin request") + root.set_attribute('status', str(Status.INVALID_PIN)) + return root + + session = self.data.local.user.create_session(userid) + + if self.config.paseli.infinite: + balance = PASELIHandler.INFINITE_PASELI_AMOUNT + else: + if self.config.machine.arcade is None: + # There's no arcade for this machine, but infinite is not + # enabled, so there's no way to find a balance. + balance = 0 + else: + balance = self.data.local.user.get_balance(userid, self.config.machine.arcade) + + root.add_child(Node.s16('sequence', 0)) + root.add_child(Node.u8('acstatus', 0)) + root.add_child(Node.string('acid', 'DUMMY_ID')) + root.add_child(Node.string('acname', 'DUMMY_NAME')) + root.add_child(Node.s32('balance', balance)) + root.add_child(Node.string('sessid', session)) + return root + + def handle_eacoin_opcheckin_request(self, request: Node) -> Node: + root = Node.void('eacoin') + passwd = request.child_value('passwd') + + if passwd is None: + # Refuse to return anything + print("Invalid eacoin checkin request, missing passwd") + root.set_attribute('status', str(Status.NO_PROFILE)) + return root + + if self.config.machine.arcade is None: + # Machine doesn't belong to an arcade + print("Machine doesn't belong to an arcade") + root.set_attribute('status', str(Status.NO_PROFILE)) + return root + + arcade = self.data.local.machine.get_arcade(self.config.machine.arcade) + if arcade is None: + # Refuse to do anything + print("No arcade for operator checkin request") + root.set_attribute('status', str(Status.NO_PROFILE)) + return root + + if arcade.pin != passwd: + # Refuse to do anything + print("User entered invalid pin for operator checkin request") + root.set_attribute('status', str(Status.INVALID_PIN)) + return root + + session = self.data.local.machine.create_session(arcade.id) + root.add_child(Node.string('sessid', session)) + return root + + def handle_eacoin_consume_request(self, request: Node) -> Node: + + def make_resp(status: int, balance: int) -> Node: + root = Node.void('eacoin') + root.add_child(Node.u8('acstatus', status)) + root.add_child(Node.u8('autocharge', 0)) + root.add_child(Node.s32('balance', balance)) + return root + + session = request.child_value('sessid') + payment = request.child_value('payment') + service = request.child_value('service') + details = request.child_value('detail') + if session is None or payment is None: + # Refuse to do anything + print("Invalid eacoin consume request, missing sessid or payment") + return make_resp(2, 0) + + userid = self.data.local.user.from_session(session) + if userid is None: + # Refuse to do anything + print("Invalid session for eacoin consume request") + return make_resp(2, 0) + + if self.config.paseli.infinite: + balance = PASELIHandler.INFINITE_PASELI_AMOUNT - payment + else: + if self.config.machine.arcade is None: + # There's no arcade for this machine, but infinite is not + # enabled, so there's no way to find a balance, assume failed + # consume payment. + balance = None + else: + # Look up the new balance based on this delta. If there isn't enough, + # we will end up returning None here and exit without performing. + balance = self.data.local.user.update_balance(userid, self.config.machine.arcade, -payment) + + if balance is None: + print("Not enough balance for eacoin consume request") + return make_resp(1, self.data.local.user.get_balance(userid, self.config.machine.arcade)) + else: + self.data.local.network.put_event( + 'paseli_transaction', + { + 'delta': -payment, + 'balance': balance, + 'service': -service, + 'reason': details, + 'pcbid': self.config.machine.pcbid, + }, + userid=userid, + arcadeid=self.config.machine.arcade, + ) + + return make_resp(0, balance) + + def handle_eacoin_getlog_request(self, request: Node) -> Node: + root = Node.void('eacoin') + sessid = request.child_value('sessid') + logtype = request.child_value('logtype') + target = request.child_value('target') + limit = request.child_value('perpage') + offset = request.child_value('offset') + + # Try to determine whether its a user or an arcade session + userid = self.data.local.user.from_session(sessid) + if userid is None: + arcadeid = self.data.local.machine.from_session(sessid) + else: + arcadeid = None + + # Bail out if we don't have any idea what session this is + if userid is None and arcadeid is None: + print("Unable to determine session type") + return root + + # If we're a user session, also look up the current arcade + # so we display only entries that happened on this arcade. + if userid is not None: + arcade = self.data.local.machine.get_arcade(self.config.machine.arcade) + if arcade is None: + print("Machine doesn't belong to an arcade") + return root + arcadeid = arcade.id + + # Now, look up all transactions for this specific group + events = self.data.local.network.get_events( + userid=userid, + arcadeid=arcadeid, + event='paseli_transaction', + ) + + # Further filter it down to the current PCBID + events = [event for event in events if event.data.get('pcbid') == target] + + # Grab the end of day today as a timestamp + end_of_today = Time.end_of_today() + time_format = '%Y-%m-%d %H:%M:%S' + date_format = '%Y-%m-%d' + + # Set up common structure + lognode = Node.void(logtype) + topic = Node.void('topic') + lognode.add_child(topic) + summary = Node.void('summary') + lognode.add_child(summary) + + # Display what day we are summed to + topic.add_child(Node.string('sumdate', Time.format(Time.now(), date_format))) + + if logtype == 'last7days': + # We show today in the today total, last 7 days prior in the week total + beginning_of_today = end_of_today - Time.SECONDS_IN_DAY + end_of_week = beginning_of_today + beginning_of_week = end_of_week - Time.SECONDS_IN_WEEK + + topic.add_child(Node.string('sumfrom', Time.format(beginning_of_week, date_format))) + topic.add_child(Node.string('sumto', Time.format(end_of_week, date_format))) + today_total = sum([ + -event.data.get_int('delta') for event in events + if event.timestamp >= beginning_of_today and event.timestamp < end_of_today + ]) + + today_total = sum([ + -event.data.get_int('delta') for event in events + if event.timestamp >= beginning_of_today and event.timestamp < end_of_today + ]) + week_txns = [ + -event.data.get_int('delta') for event in events + if event.timestamp >= beginning_of_week and event.timestamp < end_of_week + ] + week_total = sum(week_txns) + if len(week_txns) > 0: + week_avg = int(sum(week_txns) / len(week_txns)) + else: + week_avg = 0 + + # We display the totals for each day starting with yesterday and up through 7 days prior. + # Index starts at 0 = yesterday, 1 = the day before, etc... + items = [] + for days in range(0, 7): + end_of_day = end_of_week - (days * Time.SECONDS_IN_DAY) + start_of_day = end_of_day - Time.SECONDS_IN_DAY + + items.append(sum([ + -event.data.get_int('delta') for event in events + if event.timestamp >= start_of_day and event.timestamp < end_of_day + ])) + + topic.add_child(Node.s32('today', today_total)) + topic.add_child(Node.s32('average', week_avg)) + topic.add_child(Node.s32('total', week_total)) + summary.add_child(Node.s32_array('items', items)) + + if logtype == 'last52weeks': + # Start one week back, since the operator can look at last7days for newer stuff. + beginning_of_today = end_of_today - Time.SECONDS_IN_DAY + end_of_52_weeks = beginning_of_today - Time.SECONDS_IN_WEEK + + topic.add_child(Node.string('sumfrom', Time.format(end_of_52_weeks - (52 * Time.SECONDS_IN_WEEK), date_format))) + topic.add_child(Node.string('sumto', Time.format(end_of_52_weeks, date_format))) + + # We index backwards, where index 0 = the first week back, 1 = the next week back after that, etc... + items = [] + for weeks in range(0, 52): + end_of_range = end_of_52_weeks - (weeks * Time.SECONDS_IN_WEEK) + beginning_of_range = end_of_range - Time.SECONDS_IN_WEEK + + items.append(sum([ + -event.data.get_int('delta') for event in events + if event.timestamp >= beginning_of_range and event.timestamp < end_of_range + ])) + + summary.add_child(Node.s32_array('items', items)) + + if logtype == 'eachday': + start_ts = Time.now() + end_ts = Time.now() + weekdays = [0] * 7 + + for event in events: + event_day = Time.days_into_week(event.timestamp) + weekdays[event_day] = weekdays[event_day] - event.data.get_int('delta') + if event.timestamp < start_ts: + start_ts = event.timestamp + + topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format))) + topic.add_child(Node.string('sumto', Time.format(end_ts, date_format))) + summary.add_child(Node.s32_array('items', weekdays)) + + if logtype == 'eachhour': + start_ts = Time.now() + end_ts = Time.now() + hours = [0] * 24 + + for event in events: + event_hour = int((event.timestamp % Time.SECONDS_IN_DAY) / Time.SECONDS_IN_HOUR) + hours[event_hour] = hours[event_hour] - event.data.get_int('delta') + if event.timestamp < start_ts: + start_ts = event.timestamp + + topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format))) + topic.add_child(Node.string('sumto', Time.format(end_ts, date_format))) + summary.add_child(Node.s32_array('items', hours)) + + if logtype == 'detail': + history = Node.void('history') + lognode.add_child(history) + + # Respect details paging + if offset is not None: + events = events[offset:] + if limit is not None: + events = events[:limit] + + # Output the details themselves + for event in events: + card_no = '' + if event.userid is not None: + user = self.data.local.user.get_user(event.userid) + if user is not None: + cards = self.data.local.user.get_cards(user.id) + if len(cards) > 0: + card_no = CardCipher.encode(cards[0]) + + item = Node.void('item') + history.add_child(item) + item.add_child(Node.string('date', Time.format(event.timestamp, time_format))) + item.add_child(Node.s32('consume', -event.data.get_int('delta'))) + item.add_child(Node.s32('service', -event.data.get_int('service'))) + item.add_child(Node.string('cardtype', '')) + item.add_child(Node.string('cardno', ' ' * self.paseli_padding + card_no)) + item.add_child(Node.string('title', '')) + item.add_child(Node.string('systemid', '')) + + if logtype == 'lastmonths': + year, month, _ = Time.todays_date() + this_month = Time.timestamp_from_date(year, month) + last_month = Time.timestamp_from_date(year, month - 1) + month_before = Time.timestamp_from_date(year, month - 2) + + topic.add_child(Node.string('sumfrom', Time.format(month_before, date_format))) + topic.add_child(Node.string('sumto', Time.format(this_month, date_format))) + + for (start, end) in [(month_before, last_month), (last_month, this_month)]: + year, month, _ = Time.date_from_timestamp(start) + + items = [] + for day in range(0, 31): + begin_ts = start + (day * Time.SECONDS_IN_DAY) + end_ts = begin_ts + Time.SECONDS_IN_DAY + if begin_ts >= end: + # Passed the end of this month + items.append(0) + else: + # Sum up all the txns for this day + items.append(sum([ + -event.data.get_int('delta') for event in events + if event.timestamp >= begin_ts and event.timestamp < end_ts + ])) + + item = Node.void('item') + summary.add_child(item) + item.add_child(Node.s32('year', year)) + item.add_child(Node.s32('month', month)) + item.add_child(Node.s32_array('items', items)) + + root.add_child(Node.u8('processing', 0)) + root.add_child(lognode) + return root + + def handle_eacoin_opchpass_request(self, request: Node) -> Node: + root = Node.void('eacoin') + oldpass = request.child_value('passwd') + newpass = request.child_value('newpasswd') + + if oldpass is None or newpass is None: + # Refuse to return anything + print("Invalid eacoin pass change request, missing passwd") + root.set_attribute('status', str(Status.NO_PROFILE)) + return root + + if self.config.machine.arcade is None: + # Machine doesn't belong to an arcade + print("Machine doesn't belong to an arcade") + root.set_attribute('status', str(Status.NO_PROFILE)) + return root + + arcade = self.data.local.machine.get_arcade(self.config.machine.arcade) + if arcade is None: + # Refuse to do anything + print("No arcade for operator pass change request") + root.set_attribute('status', str(Status.NO_PROFILE)) + return root + + if arcade.pin != oldpass: + # Refuse to do anything + print("User entered invalid pin for operator pass change request") + root.set_attribute('status', str(Status.INVALID_PIN)) + return root + + arcade.pin = newpass + self.data.local.machine.put_arcade(arcade) + return root + + def handle_eacoin_checkout_request(self, request: Node) -> Node: + session = request.child_value('sessid') + if session is not None: + # Destroy the session so it can't be used for any other purchases + self.data.local.user.destroy_session(session) + + root = Node.void('eacoin') + return root + + def handle_eacoin_opcheckout_request(self, request: Node) -> Node: + session = request.child_value('sessid') + if session is not None: + # Destroy the session so it can't be used for any other purchases + self.data.local.machine.destroy_session(session) + + root = Node.void('eacoin') + return root diff --git a/bemani/backend/ddr/base.py b/bemani/backend/ddr/base.py index cb70ae3..af613b7 100644 --- a/bemani/backend/ddr/base.py +++ b/bemani/backend/ddr/base.py @@ -1,5 +1,6 @@ # vim: set fileencoding=utf-8 from typing import Optional, List +from typing_extensions import Final from bemani.backend.base import Base from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler @@ -14,42 +15,42 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): profiles based on refid, creating new profiles, looking up and saving scores. """ - game = GameConstants.DDR + game: GameConstants = GameConstants.DDR - HALO_NONE = DBConstants.DDR_HALO_NONE - HALO_GOOD_FULL_COMBO = DBConstants.DDR_HALO_GOOD_FULL_COMBO - HALO_GREAT_FULL_COMBO = DBConstants.DDR_HALO_GREAT_FULL_COMBO - HALO_PERFECT_FULL_COMBO = DBConstants.DDR_HALO_PERFECT_FULL_COMBO - HALO_MARVELOUS_FULL_COMBO = DBConstants.DDR_HALO_MARVELOUS_FULL_COMBO + HALO_NONE: Final[int] = DBConstants.DDR_HALO_NONE + HALO_GOOD_FULL_COMBO: Final[int] = DBConstants.DDR_HALO_GOOD_FULL_COMBO + HALO_GREAT_FULL_COMBO: Final[int] = DBConstants.DDR_HALO_GREAT_FULL_COMBO + HALO_PERFECT_FULL_COMBO: Final[int] = DBConstants.DDR_HALO_PERFECT_FULL_COMBO + HALO_MARVELOUS_FULL_COMBO: Final[int] = DBConstants.DDR_HALO_MARVELOUS_FULL_COMBO - RANK_E = DBConstants.DDR_RANK_E - RANK_D = DBConstants.DDR_RANK_D - RANK_D_PLUS = DBConstants.DDR_RANK_D_PLUS - RANK_C_MINUS = DBConstants.DDR_RANK_C_MINUS - RANK_C = DBConstants.DDR_RANK_C - RANK_C_PLUS = DBConstants.DDR_RANK_C_PLUS - RANK_B_MINUS = DBConstants.DDR_RANK_B_MINUS - RANK_B = DBConstants.DDR_RANK_B - RANK_B_PLUS = DBConstants.DDR_RANK_B_PLUS - RANK_A_MINUS = DBConstants.DDR_RANK_A_MINUS - RANK_A = DBConstants.DDR_RANK_A - RANK_A_PLUS = DBConstants.DDR_RANK_A_PLUS - RANK_AA_MINUS = DBConstants.DDR_RANK_AA_MINUS - RANK_AA = DBConstants.DDR_RANK_AA - RANK_AA_PLUS = DBConstants.DDR_RANK_AA_PLUS - RANK_AAA = DBConstants.DDR_RANK_AAA + RANK_E: Final[int] = DBConstants.DDR_RANK_E + RANK_D: Final[int] = DBConstants.DDR_RANK_D + RANK_D_PLUS: Final[int] = DBConstants.DDR_RANK_D_PLUS + RANK_C_MINUS: Final[int] = DBConstants.DDR_RANK_C_MINUS + RANK_C: Final[int] = DBConstants.DDR_RANK_C + RANK_C_PLUS: Final[int] = DBConstants.DDR_RANK_C_PLUS + RANK_B_MINUS: Final[int] = DBConstants.DDR_RANK_B_MINUS + RANK_B: Final[int] = DBConstants.DDR_RANK_B + RANK_B_PLUS: Final[int] = DBConstants.DDR_RANK_B_PLUS + RANK_A_MINUS: Final[int] = DBConstants.DDR_RANK_A_MINUS + RANK_A: Final[int] = DBConstants.DDR_RANK_A + RANK_A_PLUS: Final[int] = DBConstants.DDR_RANK_A_PLUS + RANK_AA_MINUS: Final[int] = DBConstants.DDR_RANK_AA_MINUS + RANK_AA: Final[int] = DBConstants.DDR_RANK_AA + RANK_AA_PLUS: Final[int] = DBConstants.DDR_RANK_AA_PLUS + RANK_AAA: Final[int] = DBConstants.DDR_RANK_AAA # These constants must agree with read.py for importing charts from the game. - CHART_SINGLE_BEGINNER = 0 - CHART_SINGLE_BASIC = 1 - CHART_SINGLE_DIFFICULT = 2 - CHART_SINGLE_EXPERT = 3 - CHART_SINGLE_CHALLENGE = 4 - CHART_DOUBLE_BEGINNER = 5 - CHART_DOUBLE_BASIC = 6 - CHART_DOUBLE_DIFFICULT = 7 - CHART_DOUBLE_EXPERT = 8 - CHART_DOUBLE_CHALLENGE = 9 + CHART_SINGLE_BEGINNER: Final[int] = 0 + CHART_SINGLE_BASIC: Final[int] = 1 + CHART_SINGLE_DIFFICULT: Final[int] = 2 + CHART_SINGLE_EXPERT: Final[int] = 3 + CHART_SINGLE_CHALLENGE: Final[int] = 4 + CHART_DOUBLE_BEGINNER: Final[int] = 5 + CHART_DOUBLE_BASIC: Final[int] = 6 + CHART_DOUBLE_DIFFICULT: Final[int] = 7 + CHART_DOUBLE_EXPERT: Final[int] = 8 + CHART_DOUBLE_CHALLENGE: Final[int] = 9 # Return the local2 service so that DDR Ace will send certain packets. extra_services: List[str] = [ diff --git a/bemani/backend/ddr/ddr2013.py b/bemani/backend/ddr/ddr2013.py index 2dc3360..6c1292d 100644 --- a/bemani/backend/ddr/ddr2013.py +++ b/bemani/backend/ddr/ddr2013.py @@ -1,6 +1,6 @@ # vim: set fileencoding=utf-8 -import copy from typing import Dict, List, Optional +from typing_extensions import Final from bemani.backend.ddr.base import DDRBase from bemani.backend.ddr.ddrx3 import DDRX3 @@ -53,38 +53,38 @@ class DDR2013( DDRBase, ): - name = 'DanceDanceRevolution 2013' - version = VersionConstants.DDR_2013 + name: str = 'DanceDanceRevolution 2013' + version: int = VersionConstants.DDR_2013 - GAME_STYLE_SINGLE = 0 - GAME_STYLE_DOUBLE = 1 - GAME_STYLE_VERSUS = 2 + GAME_STYLE_SINGLE: Final[int] = 0 + GAME_STYLE_DOUBLE: Final[int] = 1 + GAME_STYLE_VERSUS: Final[int] = 2 - GAME_RANK_AAA = 1 - GAME_RANK_AA = 2 - GAME_RANK_A = 3 - GAME_RANK_B = 4 - GAME_RANK_C = 5 - GAME_RANK_D = 6 - GAME_RANK_E = 7 + GAME_RANK_AAA: Final[int] = 1 + GAME_RANK_AA: Final[int] = 2 + GAME_RANK_A: Final[int] = 3 + GAME_RANK_B: Final[int] = 4 + GAME_RANK_C: Final[int] = 5 + GAME_RANK_D: Final[int] = 6 + GAME_RANK_E: Final[int] = 7 - GAME_CHART_SINGLE_BEGINNER = 0 - GAME_CHART_SINGLE_BASIC = 1 - GAME_CHART_SINGLE_DIFFICULT = 2 - GAME_CHART_SINGLE_EXPERT = 3 - GAME_CHART_SINGLE_CHALLENGE = 4 - GAME_CHART_DOUBLE_BASIC = 5 - GAME_CHART_DOUBLE_DIFFICULT = 6 - GAME_CHART_DOUBLE_EXPERT = 7 - GAME_CHART_DOUBLE_CHALLENGE = 8 + GAME_CHART_SINGLE_BEGINNER: Final[int] = 0 + GAME_CHART_SINGLE_BASIC: Final[int] = 1 + GAME_CHART_SINGLE_DIFFICULT: Final[int] = 2 + GAME_CHART_SINGLE_EXPERT: Final[int] = 3 + GAME_CHART_SINGLE_CHALLENGE: Final[int] = 4 + GAME_CHART_DOUBLE_BASIC: Final[int] = 5 + GAME_CHART_DOUBLE_DIFFICULT: Final[int] = 6 + GAME_CHART_DOUBLE_EXPERT: Final[int] = 7 + GAME_CHART_DOUBLE_CHALLENGE: Final[int] = 8 - GAME_HALO_NONE = 0 - GAME_HALO_GREAT_COMBO = 1 - GAME_HALO_PERFECT_COMBO = 2 - GAME_HALO_MARVELOUS_COMBO = 3 - GAME_HALO_GOOD_COMBO = 4 + GAME_HALO_NONE: Final[int] = 0 + GAME_HALO_GREAT_COMBO: Final[int] = 1 + GAME_HALO_PERFECT_COMBO: Final[int] = 2 + GAME_HALO_MARVELOUS_COMBO: Final[int] = 3 + GAME_HALO_GOOD_COMBO: Final[int] = 4 - GAME_MAX_SONGS = 700 + GAME_MAX_SONGS: Final[int] = 700 def previous_version(self) -> Optional[DDRBase]: return DDRX3(self.data, self.config, self.model) @@ -468,7 +468,7 @@ class DDR2013( return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() play_stats = self.get_play_statistics(userid) # Grab last node and accessories so we can make decisions based on type diff --git a/bemani/backend/ddr/ddr2014.py b/bemani/backend/ddr/ddr2014.py index 524f28d..13d5bf2 100644 --- a/bemani/backend/ddr/ddr2014.py +++ b/bemani/backend/ddr/ddr2014.py @@ -1,6 +1,6 @@ # vim: set fileencoding=utf-8 -import copy from typing import Dict, List, Optional +from typing_extensions import Final from bemani.backend.ddr.base import DDRBase from bemani.backend.ddr.ddr2013 import DDR2013 @@ -47,38 +47,38 @@ class DDR2014( DDRBase, ): - name = 'DanceDanceRevolution 2014' - version = VersionConstants.DDR_2014 + name: str = 'DanceDanceRevolution 2014' + version: int = VersionConstants.DDR_2014 - GAME_STYLE_SINGLE = 0 - GAME_STYLE_DOUBLE = 1 - GAME_STYLE_VERSUS = 2 + GAME_STYLE_SINGLE: Final[int] = 0 + GAME_STYLE_DOUBLE: Final[int] = 1 + GAME_STYLE_VERSUS: Final[int] = 2 - GAME_RANK_AAA = 1 - GAME_RANK_AA = 2 - GAME_RANK_A = 3 - GAME_RANK_B = 4 - GAME_RANK_C = 5 - GAME_RANK_D = 6 - GAME_RANK_E = 7 + GAME_RANK_AAA: Final[int] = 1 + GAME_RANK_AA: Final[int] = 2 + GAME_RANK_A: Final[int] = 3 + GAME_RANK_B: Final[int] = 4 + GAME_RANK_C: Final[int] = 5 + GAME_RANK_D: Final[int] = 6 + GAME_RANK_E: Final[int] = 7 - GAME_CHART_SINGLE_BEGINNER = 0 - GAME_CHART_SINGLE_BASIC = 1 - GAME_CHART_SINGLE_DIFFICULT = 2 - GAME_CHART_SINGLE_EXPERT = 3 - GAME_CHART_SINGLE_CHALLENGE = 4 - GAME_CHART_DOUBLE_BASIC = 5 - GAME_CHART_DOUBLE_DIFFICULT = 6 - GAME_CHART_DOUBLE_EXPERT = 7 - GAME_CHART_DOUBLE_CHALLENGE = 8 + GAME_CHART_SINGLE_BEGINNER: Final[int] = 0 + GAME_CHART_SINGLE_BASIC: Final[int] = 1 + GAME_CHART_SINGLE_DIFFICULT: Final[int] = 2 + GAME_CHART_SINGLE_EXPERT: Final[int] = 3 + GAME_CHART_SINGLE_CHALLENGE: Final[int] = 4 + GAME_CHART_DOUBLE_BASIC: Final[int] = 5 + GAME_CHART_DOUBLE_DIFFICULT: Final[int] = 6 + GAME_CHART_DOUBLE_EXPERT: Final[int] = 7 + GAME_CHART_DOUBLE_CHALLENGE: Final[int] = 8 - GAME_HALO_NONE = 0 - GAME_HALO_GREAT_COMBO = 1 - GAME_HALO_PERFECT_COMBO = 2 - GAME_HALO_MARVELOUS_COMBO = 3 - GAME_HALO_GOOD_COMBO = 4 + GAME_HALO_NONE: Final[int] = 0 + GAME_HALO_GREAT_COMBO: Final[int] = 1 + GAME_HALO_PERFECT_COMBO: Final[int] = 2 + GAME_HALO_MARVELOUS_COMBO: Final[int] = 3 + GAME_HALO_GOOD_COMBO: Final[int] = 4 - GAME_MAX_SONGS = 800 + GAME_MAX_SONGS: Final[int] = 800 def previous_version(self) -> Optional[DDRBase]: return DDR2013(self.data, self.config, self.model) @@ -529,7 +529,7 @@ class DDR2014( return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() play_stats = self.get_play_statistics(userid) # Grab last node and accessories so we can make decisions based on type diff --git a/bemani/backend/ddr/ddra20.py b/bemani/backend/ddr/ddra20.py index 0bfca00..cb3245d 100644 --- a/bemani/backend/ddr/ddra20.py +++ b/bemani/backend/ddr/ddra20.py @@ -10,8 +10,8 @@ class DDRA20( DDRBase, ): - name = 'DanceDanceRevolution A20' - version = VersionConstants.DDR_A20 + name: str = 'DanceDanceRevolution A20' + version: int = VersionConstants.DDR_A20 def previous_version(self) -> Optional[DDRBase]: return DDRAce(self.data, self.config, self.model) diff --git a/bemani/backend/ddr/ddrace.py b/bemani/backend/ddr/ddrace.py index 40d7531..0860900 100644 --- a/bemani/backend/ddr/ddrace.py +++ b/bemani/backend/ddr/ddrace.py @@ -1,6 +1,7 @@ # vim: set fileencoding=utf-8 import base64 from typing import Dict, List, Optional +from typing_extensions import Final from bemani.backend.ess import EventLogHandler from bemani.backend.ddr.base import DDRBase @@ -15,91 +16,91 @@ class DDRAce( EventLogHandler, ): - name = 'DanceDanceRevolution A' - version = VersionConstants.DDR_ACE + name: str = 'DanceDanceRevolution A' + version: int = VersionConstants.DDR_ACE - GAME_STYLE_SINGLE = 0 - GAME_STYLE_DOUBLE = 1 - GAME_STYLE_VERSUS = 2 + GAME_STYLE_SINGLE: Final[int] = 0 + GAME_STYLE_DOUBLE: Final[int] = 1 + GAME_STYLE_VERSUS: Final[int] = 2 - GAME_RIVAL_TYPE_RIVAL3 = 32 - GAME_RIVAL_TYPE_RIVAL2 = 16 - GAME_RIVAL_TYPE_RIVAL1 = 8 - GAME_RIVAL_TYPE_WORLD = 4 - GAME_RIVAL_TYPE_AREA = 2 - GAME_RIVAL_TYPE_MACHINE = 1 + GAME_RIVAL_TYPE_RIVAL3: Final[int] = 32 + GAME_RIVAL_TYPE_RIVAL2: Final[int] = 16 + GAME_RIVAL_TYPE_RIVAL1: Final[int] = 8 + GAME_RIVAL_TYPE_WORLD: Final[int] = 4 + GAME_RIVAL_TYPE_AREA: Final[int] = 2 + GAME_RIVAL_TYPE_MACHINE: Final[int] = 1 - GAME_CHART_SINGLE_BEGINNER = 0 - GAME_CHART_SINGLE_BASIC = 1 - GAME_CHART_SINGLE_DIFFICULT = 2 - GAME_CHART_SINGLE_EXPERT = 3 - GAME_CHART_SINGLE_CHALLENGE = 4 - GAME_CHART_DOUBLE_BASIC = 5 - GAME_CHART_DOUBLE_DIFFICULT = 6 - GAME_CHART_DOUBLE_EXPERT = 7 - GAME_CHART_DOUBLE_CHALLENGE = 8 + GAME_CHART_SINGLE_BEGINNER: Final[int] = 0 + GAME_CHART_SINGLE_BASIC: Final[int] = 1 + GAME_CHART_SINGLE_DIFFICULT: Final[int] = 2 + GAME_CHART_SINGLE_EXPERT: Final[int] = 3 + GAME_CHART_SINGLE_CHALLENGE: Final[int] = 4 + GAME_CHART_DOUBLE_BASIC: Final[int] = 5 + GAME_CHART_DOUBLE_DIFFICULT: Final[int] = 6 + GAME_CHART_DOUBLE_EXPERT: Final[int] = 7 + GAME_CHART_DOUBLE_CHALLENGE: Final[int] = 8 - GAME_HALO_NONE = 6 - GAME_HALO_GOOD_COMBO = 7 - GAME_HALO_GREAT_COMBO = 8 - GAME_HALO_PERFECT_COMBO = 9 - GAME_HALO_MARVELOUS_COMBO = 10 + GAME_HALO_NONE: Final[int] = 6 + GAME_HALO_GOOD_COMBO: Final[int] = 7 + GAME_HALO_GREAT_COMBO: Final[int] = 8 + GAME_HALO_PERFECT_COMBO: Final[int] = 9 + GAME_HALO_MARVELOUS_COMBO: Final[int] = 10 - GAME_RANK_E = 15 - GAME_RANK_D = 14 - GAME_RANK_D_PLUS = 13 - GAME_RANK_C_MINUS = 12 - GAME_RANK_C = 11 - GAME_RANK_C_PLUS = 10 - GAME_RANK_B_MINUS = 9 - GAME_RANK_B = 8 - GAME_RANK_B_PLUS = 7 - GAME_RANK_A_MINUS = 6 - GAME_RANK_A = 5 - GAME_RANK_A_PLUS = 4 - GAME_RANK_AA_MINUS = 3 - GAME_RANK_AA = 2 - GAME_RANK_AA_PLUS = 1 - GAME_RANK_AAA = 0 + GAME_RANK_E: Final[int] = 15 + GAME_RANK_D: Final[int] = 14 + GAME_RANK_D_PLUS: Final[int] = 13 + GAME_RANK_C_MINUS: Final[int] = 12 + GAME_RANK_C: Final[int] = 11 + GAME_RANK_C_PLUS: Final[int] = 10 + GAME_RANK_B_MINUS: Final[int] = 9 + GAME_RANK_B: Final[int] = 8 + GAME_RANK_B_PLUS: Final[int] = 7 + GAME_RANK_A_MINUS: Final[int] = 6 + GAME_RANK_A: Final[int] = 5 + GAME_RANK_A_PLUS: Final[int] = 4 + GAME_RANK_AA_MINUS: Final[int] = 3 + GAME_RANK_AA: Final[int] = 2 + GAME_RANK_AA_PLUS: Final[int] = 1 + GAME_RANK_AAA: Final[int] = 0 - GAME_MAX_SONGS = 1024 + GAME_MAX_SONGS: Final[int] = 1024 - GAME_COMMON_AREA_OFFSET = 1 - GAME_COMMON_WEIGHT_DISPLAY_OFFSET = 3 - GAME_COMMON_CHARACTER_OFFSET = 4 - GAME_COMMON_EXTRA_CHARGE_OFFSET = 5 - GAME_COMMON_TOTAL_PLAYS_OFFSET = 9 - GAME_COMMON_SINGLE_PLAYS_OFFSET = 11 - GAME_COMMON_DOUBLE_PLAYS_OFFSET = 12 - GAME_COMMON_WEIGHT_OFFSET = 17 - GAME_COMMON_NAME_OFFSET = 25 - GAME_COMMON_SEQ_OFFSET = 26 + GAME_COMMON_AREA_OFFSET: Final[int] = 1 + GAME_COMMON_WEIGHT_DISPLAY_OFFSET: Final[int] = 3 + GAME_COMMON_CHARACTER_OFFSET: Final[int] = 4 + GAME_COMMON_EXTRA_CHARGE_OFFSET: Final[int] = 5 + GAME_COMMON_TOTAL_PLAYS_OFFSET: Final[int] = 9 + GAME_COMMON_SINGLE_PLAYS_OFFSET: Final[int] = 11 + GAME_COMMON_DOUBLE_PLAYS_OFFSET: Final[int] = 12 + GAME_COMMON_WEIGHT_OFFSET: Final[int] = 17 + GAME_COMMON_NAME_OFFSET: Final[int] = 25 + GAME_COMMON_SEQ_OFFSET: Final[int] = 26 - GAME_OPTION_SCROLL_OFFSET = 1 - GAME_OPTION_BOOST_OFFSET = 2 - GAME_OPTION_APPEARANCE_OFFSET = 3 - GAME_OPTION_TURN_OFFSET = 4 - GAME_OPTION_STEP_ZONE_OFFSET = 5 - GAME_OPTION_SCROLL_OFFSET = 6 - GAME_OPTION_ARROW_COLOR_OFFSET = 7 - GAME_OPTION_CUT_OFFSET = 8 - GAME_OPTION_FREEZE_OFFSET = 9 - GAME_OPTION_JUMPS_OFFSET = 10 - GAME_OPTION_ARROW_SKIN_OFFSET = 11 - GAME_OPTION_FILTER_OFFSET = 12 - GAME_OPTION_GUIDELINE_OFFSET = 13 - GAME_OPTION_GAUGE_OFFSET = 14 - GAME_OPTION_COMBO_POSITION_OFFSET = 15 - GAME_OPTION_FAST_SLOW_OFFSET = 16 + GAME_OPTION_SPEED_OFFSET: Final[int] = 1 + GAME_OPTION_BOOST_OFFSET: Final[int] = 2 + GAME_OPTION_APPEARANCE_OFFSET: Final[int] = 3 + GAME_OPTION_TURN_OFFSET: Final[int] = 4 + GAME_OPTION_STEP_ZONE_OFFSET: Final[int] = 5 + GAME_OPTION_SCROLL_OFFSET: Final[int] = 6 + GAME_OPTION_ARROW_COLOR_OFFSET: Final[int] = 7 + GAME_OPTION_CUT_OFFSET: Final[int] = 8 + GAME_OPTION_FREEZE_OFFSET: Final[int] = 9 + GAME_OPTION_JUMPS_OFFSET: Final[int] = 10 + GAME_OPTION_ARROW_SKIN_OFFSET: Final[int] = 11 + GAME_OPTION_FILTER_OFFSET: Final[int] = 12 + GAME_OPTION_GUIDELINE_OFFSET: Final[int] = 13 + GAME_OPTION_GAUGE_OFFSET: Final[int] = 14 + GAME_OPTION_COMBO_POSITION_OFFSET: Final[int] = 15 + GAME_OPTION_FAST_SLOW_OFFSET: Final[int] = 16 - GAME_LAST_CALORIES_OFFSET = 10 + GAME_LAST_CALORIES_OFFSET: Final[int] = 10 - GAME_RIVAL_SLOT_1_ACTIVE_OFFSET = 1 - GAME_RIVAL_SLOT_2_ACTIVE_OFFSET = 2 - GAME_RIVAL_SLOT_3_ACTIVE_OFFSET = 3 - GAME_RIVAL_SLOT_1_DDRCODE_OFFSET = 9 - GAME_RIVAL_SLOT_2_DDRCODE_OFFSET = 10 - GAME_RIVAL_SLOT_3_DDRCODE_OFFSET = 11 + GAME_RIVAL_SLOT_1_ACTIVE_OFFSET: Final[int] = 1 + GAME_RIVAL_SLOT_2_ACTIVE_OFFSET: Final[int] = 2 + GAME_RIVAL_SLOT_3_ACTIVE_OFFSET: Final[int] = 3 + GAME_RIVAL_SLOT_1_DDRCODE_OFFSET: Final[int] = 9 + GAME_RIVAL_SLOT_2_DDRCODE_OFFSET: Final[int] = 10 + GAME_RIVAL_SLOT_3_DDRCODE_OFFSET: Final[int] = 11 def previous_version(self) -> Optional[DDRBase]: return DDR2014(self.data, self.config, self.model) diff --git a/bemani/backend/ddr/ddrx2.py b/bemani/backend/ddr/ddrx2.py index 8bfc620..14ce96a 100644 --- a/bemani/backend/ddr/ddrx2.py +++ b/bemani/backend/ddr/ddrx2.py @@ -1,6 +1,6 @@ # vim: set fileencoding=utf-8 -import copy from typing import Dict, List, Optional, Tuple +from typing_extensions import Final from bemani.backend.ddr.base import DDRBase from bemani.backend.ddr.stubs import DDRX @@ -43,37 +43,37 @@ class DDRX2( DDRBase, ): - name = 'DanceDanceRevolution X2' - version = VersionConstants.DDR_X2 + name: str = 'DanceDanceRevolution X2' + version: int = VersionConstants.DDR_X2 - GAME_STYLE_SINGLE = 0 - GAME_STYLE_DOUBLE = 1 - GAME_STYLE_VERSUS = 2 + GAME_STYLE_SINGLE: Final[int] = 0 + GAME_STYLE_DOUBLE: Final[int] = 1 + GAME_STYLE_VERSUS: Final[int] = 2 - GAME_RANK_AAA = 1 - GAME_RANK_AA = 2 - GAME_RANK_A = 3 - GAME_RANK_B = 4 - GAME_RANK_C = 5 - GAME_RANK_D = 6 - GAME_RANK_E = 7 + GAME_RANK_AAA: Final[int] = 1 + GAME_RANK_AA: Final[int] = 2 + GAME_RANK_A: Final[int] = 3 + GAME_RANK_B: Final[int] = 4 + GAME_RANK_C: Final[int] = 5 + GAME_RANK_D: Final[int] = 6 + GAME_RANK_E: Final[int] = 7 - GAME_CHART_SINGLE_BEGINNER = 0 - GAME_CHART_SINGLE_BASIC = 1 - GAME_CHART_SINGLE_DIFFICULT = 2 - GAME_CHART_SINGLE_EXPERT = 3 - GAME_CHART_SINGLE_CHALLENGE = 4 - GAME_CHART_DOUBLE_BASIC = 5 - GAME_CHART_DOUBLE_DIFFICULT = 6 - GAME_CHART_DOUBLE_EXPERT = 7 - GAME_CHART_DOUBLE_CHALLENGE = 8 + GAME_CHART_SINGLE_BEGINNER: Final[int] = 0 + GAME_CHART_SINGLE_BASIC: Final[int] = 1 + GAME_CHART_SINGLE_DIFFICULT: Final[int] = 2 + GAME_CHART_SINGLE_EXPERT: Final[int] = 3 + GAME_CHART_SINGLE_CHALLENGE: Final[int] = 4 + GAME_CHART_DOUBLE_BASIC: Final[int] = 5 + GAME_CHART_DOUBLE_DIFFICULT: Final[int] = 6 + GAME_CHART_DOUBLE_EXPERT: Final[int] = 7 + GAME_CHART_DOUBLE_CHALLENGE: Final[int] = 8 - GAME_HALO_NONE = 0 - GAME_HALO_FULL_COMBO = 1 - GAME_HALO_PERFECT_COMBO = 2 - GAME_HALO_MARVELOUS_COMBO = 3 + GAME_HALO_NONE: Final[int] = 0 + GAME_HALO_FULL_COMBO: Final[int] = 1 + GAME_HALO_PERFECT_COMBO: Final[int] = 2 + GAME_HALO_MARVELOUS_COMBO: Final[int] = 3 - GAME_MAX_SONGS = 600 + GAME_MAX_SONGS: Final[int] = 600 def previous_version(self) -> Optional[DDRBase]: return DDRX(self.data, self.config, self.model) @@ -517,7 +517,7 @@ class DDRX2( return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() play_stats = self.get_play_statistics(userid) # Grab last node and accessories so we can make decisions based on type diff --git a/bemani/backend/ddr/ddrx3.py b/bemani/backend/ddr/ddrx3.py index b944fc8..01a5c0e 100644 --- a/bemani/backend/ddr/ddrx3.py +++ b/bemani/backend/ddr/ddrx3.py @@ -1,6 +1,6 @@ # vim: set fileencoding=utf-8 -import copy from typing import Dict, List, Optional, Union +from typing_extensions import Final from bemani.backend.ddr.base import DDRBase from bemani.backend.ddr.ddrx2 import DDRX2 @@ -49,40 +49,40 @@ class DDRX3( DDRBase, ): - name = 'DanceDanceRevolution X3 VS 2ndMIX' - version = VersionConstants.DDR_X3_VS_2NDMIX + name: str = 'DanceDanceRevolution X3 VS 2ndMIX' + version: int = VersionConstants.DDR_X3_VS_2NDMIX - GAME_STYLE_SINGLE = 0 - GAME_STYLE_DOUBLE = 1 - GAME_STYLE_VERSUS = 2 + GAME_STYLE_SINGLE: Final[int] = 0 + GAME_STYLE_DOUBLE: Final[int] = 1 + GAME_STYLE_VERSUS: Final[int] = 2 - GAME_RANK_AAA = 1 - GAME_RANK_AA = 2 - GAME_RANK_A = 3 - GAME_RANK_B = 4 - GAME_RANK_C = 5 - GAME_RANK_D = 6 - GAME_RANK_E = 7 + GAME_RANK_AAA: Final[int] = 1 + GAME_RANK_AA: Final[int] = 2 + GAME_RANK_A: Final[int] = 3 + GAME_RANK_B: Final[int] = 4 + GAME_RANK_C: Final[int] = 5 + GAME_RANK_D: Final[int] = 6 + GAME_RANK_E: Final[int] = 7 - GAME_CHART_SINGLE_BEGINNER = 0 - GAME_CHART_SINGLE_BASIC = 1 - GAME_CHART_SINGLE_DIFFICULT = 2 - GAME_CHART_SINGLE_EXPERT = 3 - GAME_CHART_SINGLE_CHALLENGE = 4 - GAME_CHART_DOUBLE_BASIC = 5 - GAME_CHART_DOUBLE_DIFFICULT = 6 - GAME_CHART_DOUBLE_EXPERT = 7 - GAME_CHART_DOUBLE_CHALLENGE = 8 + GAME_CHART_SINGLE_BEGINNER: Final[int] = 0 + GAME_CHART_SINGLE_BASIC: Final[int] = 1 + GAME_CHART_SINGLE_DIFFICULT: Final[int] = 2 + GAME_CHART_SINGLE_EXPERT: Final[int] = 3 + GAME_CHART_SINGLE_CHALLENGE: Final[int] = 4 + GAME_CHART_DOUBLE_BASIC: Final[int] = 5 + GAME_CHART_DOUBLE_DIFFICULT: Final[int] = 6 + GAME_CHART_DOUBLE_EXPERT: Final[int] = 7 + GAME_CHART_DOUBLE_CHALLENGE: Final[int] = 8 - GAME_HALO_NONE = 0 - GAME_HALO_FULL_COMBO = 1 - GAME_HALO_PERFECT_COMBO = 2 - GAME_HALO_MARVELOUS_COMBO = 3 + GAME_HALO_NONE: Final[int] = 0 + GAME_HALO_FULL_COMBO: Final[int] = 1 + GAME_HALO_PERFECT_COMBO: Final[int] = 2 + GAME_HALO_MARVELOUS_COMBO: Final[int] = 3 - GAME_PLAY_MODE_NORMAL = 1 - GAME_PLAY_MODE_2NDMIX = 5 + GAME_PLAY_MODE_NORMAL: Final[int] = 1 + GAME_PLAY_MODE_2NDMIX: Final[int] = 5 - GAME_MAX_SONGS = 700 + GAME_MAX_SONGS: Final[int] = 700 def previous_version(self) -> Optional[DDRBase]: return DDRX2(self.data, self.config, self.model) @@ -543,7 +543,7 @@ class DDRX3( return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() play_stats = self.get_play_statistics(userid) # Grab last node and accessories so we can make decisions based on type diff --git a/bemani/backend/ddr/factory.py b/bemani/backend/ddr/factory.py index e44b812..07cf2b2 100644 --- a/bemani/backend/ddr/factory.py +++ b/bemani/backend/ddr/factory.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import List, Optional, Type from bemani.backend.base import Base, Factory from bemani.backend.ddr.stubs import ( @@ -26,7 +26,7 @@ from bemani.data import Config, Data class DDRFactory(Factory): - MANAGED_CLASSES = [ + MANAGED_CLASSES: List[Type[Base]] = [ DDR1stMix, DDR2ndMix, DDR4thMix, diff --git a/bemani/backend/ddr/stubs.py b/bemani/backend/ddr/stubs.py index c90c2f9..a346123 100644 --- a/bemani/backend/ddr/stubs.py +++ b/bemani/backend/ddr/stubs.py @@ -7,8 +7,8 @@ from bemani.common import VersionConstants class DDRX(DDRBase): - name = 'DanceDanceRevolution X' - version = VersionConstants.DDR_X + name: str = 'DanceDanceRevolution X' + version: int = VersionConstants.DDR_X def previous_version(self) -> Optional[DDRBase]: return DDRSuperNova2(self.data, self.config, self.model) @@ -16,8 +16,8 @@ class DDRX(DDRBase): class DDRSuperNova2(DDRBase): - name = 'DanceDanceRevolution SuperNova 2' - version = VersionConstants.DDR_SUPERNOVA_2 + name: str = 'DanceDanceRevolution SuperNova 2' + version: int = VersionConstants.DDR_SUPERNOVA_2 def previous_version(self) -> Optional[DDRBase]: return DDRSuperNova(self.data, self.config, self.model) @@ -25,8 +25,8 @@ class DDRSuperNova2(DDRBase): class DDRSuperNova(DDRBase): - name = 'DanceDanceRevolution SuperNova' - version = VersionConstants.DDR_SUPERNOVA + name: str = 'DanceDanceRevolution SuperNova' + version: int = VersionConstants.DDR_SUPERNOVA def previous_version(self) -> Optional[DDRBase]: return DDRExtreme(self.data, self.config, self.model) @@ -34,8 +34,8 @@ class DDRSuperNova(DDRBase): class DDRExtreme(DDRBase): - name = 'DanceDanceRevolution Extreme' - version = VersionConstants.DDR_EXTREME + name: str = 'DanceDanceRevolution Extreme' + version: int = VersionConstants.DDR_EXTREME def previous_version(self) -> Optional[DDRBase]: return DDR7thMix(self.data, self.config, self.model) @@ -43,8 +43,8 @@ class DDRExtreme(DDRBase): class DDR7thMix(DDRBase): - name = 'DanceDanceRevolution 7thMix' - version = VersionConstants.DDR_7THMIX + name: str = 'DanceDanceRevolution 7thMix' + version: int = VersionConstants.DDR_7THMIX def previous_version(self) -> Optional[DDRBase]: return DDR6thMix(self.data, self.config, self.model) @@ -52,8 +52,8 @@ class DDR7thMix(DDRBase): class DDR6thMix(DDRBase): - name = 'DanceDanceRevolution 6thMix' - version = VersionConstants.DDR_6THMIX + name: str = 'DanceDanceRevolution 6thMix' + version: int = VersionConstants.DDR_6THMIX def previous_version(self) -> Optional[DDRBase]: return DDR5thMix(self.data, self.config, self.model) @@ -61,8 +61,8 @@ class DDR6thMix(DDRBase): class DDR5thMix(DDRBase): - name = 'DanceDanceRevolution 5thMix' - version = VersionConstants.DDR_5THMIX + name: str = 'DanceDanceRevolution 5thMix' + version: int = VersionConstants.DDR_5THMIX def previous_version(self) -> Optional[DDRBase]: return DDR4thMix(self.data, self.config, self.model) @@ -70,8 +70,8 @@ class DDR5thMix(DDRBase): class DDR4thMix(DDRBase): - name = 'DanceDanceRevolution 4thMix' - version = VersionConstants.DDR_4THMIX + name: str = 'DanceDanceRevolution 4thMix' + version: int = VersionConstants.DDR_4THMIX def previous_version(self) -> Optional[DDRBase]: return DDR3rdMix(self.data, self.config, self.model) @@ -79,8 +79,8 @@ class DDR4thMix(DDRBase): class DDR3rdMix(DDRBase): - name = 'DanceDanceRevolution 3rdMix' - version = VersionConstants.DDR_3RDMIX + name: str = 'DanceDanceRevolution 3rdMix' + version: int = VersionConstants.DDR_3RDMIX def previous_version(self) -> Optional[DDRBase]: return DDR2ndMix(self.data, self.config, self.model) @@ -88,8 +88,8 @@ class DDR3rdMix(DDRBase): class DDR2ndMix(DDRBase): - name = 'DanceDanceRevolution 2ndMix' - version = VersionConstants.DDR_2NDMIX + name: str = 'DanceDanceRevolution 2ndMix' + version: int = VersionConstants.DDR_2NDMIX def previous_version(self) -> Optional[DDRBase]: return DDR1stMix(self.data, self.config, self.model) @@ -97,5 +97,5 @@ class DDR2ndMix(DDRBase): class DDR1stMix(DDRBase): - name = 'DanceDanceRevolution 1stMix' - version = VersionConstants.DDR_1STMIX + name: str = 'DanceDanceRevolution 1stMix' + version: int = VersionConstants.DDR_1STMIX diff --git a/bemani/backend/iidx/base.py b/bemani/backend/iidx/base.py index d328ef0..77a346d 100644 --- a/bemani/backend/iidx/base.py +++ b/bemani/backend/iidx/base.py @@ -1,6 +1,7 @@ # vim: set fileencoding=utf-8 import struct from typing import Optional, Dict, Any, List, Tuple +from typing_extensions import Final from bemani.backend.base import Base from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler @@ -16,64 +17,64 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): scores. """ - game = GameConstants.IIDX + game: GameConstants = GameConstants.IIDX - paseli_padding = 15 + paseli_padding: int = 15 - CLEAR_TYPE_SINGLE = 1 - CLEAR_TYPE_DOUBLE = 2 + CLEAR_TYPE_SINGLE: Final[int] = 1 + CLEAR_TYPE_DOUBLE: Final[int] = 2 - CLEAR_STATUS_NO_PLAY = DBConstants.IIDX_CLEAR_STATUS_NO_PLAY - CLEAR_STATUS_FAILED = DBConstants.IIDX_CLEAR_STATUS_FAILED - CLEAR_STATUS_ASSIST_CLEAR = DBConstants.IIDX_CLEAR_STATUS_ASSIST_CLEAR - CLEAR_STATUS_EASY_CLEAR = DBConstants.IIDX_CLEAR_STATUS_EASY_CLEAR - CLEAR_STATUS_CLEAR = DBConstants.IIDX_CLEAR_STATUS_CLEAR - CLEAR_STATUS_HARD_CLEAR = DBConstants.IIDX_CLEAR_STATUS_HARD_CLEAR - CLEAR_STATUS_EX_HARD_CLEAR = DBConstants.IIDX_CLEAR_STATUS_EX_HARD_CLEAR - CLEAR_STATUS_FULL_COMBO = DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO + CLEAR_STATUS_NO_PLAY: Final[int] = DBConstants.IIDX_CLEAR_STATUS_NO_PLAY + CLEAR_STATUS_FAILED: Final[int] = DBConstants.IIDX_CLEAR_STATUS_FAILED + CLEAR_STATUS_ASSIST_CLEAR: Final[int] = DBConstants.IIDX_CLEAR_STATUS_ASSIST_CLEAR + CLEAR_STATUS_EASY_CLEAR: Final[int] = DBConstants.IIDX_CLEAR_STATUS_EASY_CLEAR + CLEAR_STATUS_CLEAR: Final[int] = DBConstants.IIDX_CLEAR_STATUS_CLEAR + CLEAR_STATUS_HARD_CLEAR: Final[int] = DBConstants.IIDX_CLEAR_STATUS_HARD_CLEAR + CLEAR_STATUS_EX_HARD_CLEAR: Final[int] = DBConstants.IIDX_CLEAR_STATUS_EX_HARD_CLEAR + CLEAR_STATUS_FULL_COMBO: Final[int] = DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO - CHART_TYPE_N7 = 0 - CHART_TYPE_H7 = 1 - CHART_TYPE_A7 = 2 - CHART_TYPE_N14 = 3 - CHART_TYPE_H14 = 4 - CHART_TYPE_A14 = 5 + CHART_TYPE_N7: Final[int] = 0 + CHART_TYPE_H7: Final[int] = 1 + CHART_TYPE_A7: Final[int] = 2 + CHART_TYPE_N14: Final[int] = 3 + CHART_TYPE_H14: Final[int] = 4 + CHART_TYPE_A14: Final[int] = 5 # Beginner charts only save status - CHART_TYPE_B7 = 6 + CHART_TYPE_B7: Final[int] = 6 - DAN_RANK_7_KYU = DBConstants.IIDX_DAN_RANK_7_KYU - DAN_RANK_6_KYU = DBConstants.IIDX_DAN_RANK_6_KYU - DAN_RANK_5_KYU = DBConstants.IIDX_DAN_RANK_5_KYU - DAN_RANK_4_KYU = DBConstants.IIDX_DAN_RANK_4_KYU - DAN_RANK_3_KYU = DBConstants.IIDX_DAN_RANK_3_KYU - DAN_RANK_2_KYU = DBConstants.IIDX_DAN_RANK_2_KYU - DAN_RANK_1_KYU = DBConstants.IIDX_DAN_RANK_1_KYU - DAN_RANK_1_DAN = DBConstants.IIDX_DAN_RANK_1_DAN - DAN_RANK_2_DAN = DBConstants.IIDX_DAN_RANK_2_DAN - DAN_RANK_3_DAN = DBConstants.IIDX_DAN_RANK_3_DAN - DAN_RANK_4_DAN = DBConstants.IIDX_DAN_RANK_4_DAN - DAN_RANK_5_DAN = DBConstants.IIDX_DAN_RANK_5_DAN - DAN_RANK_6_DAN = DBConstants.IIDX_DAN_RANK_6_DAN - DAN_RANK_7_DAN = DBConstants.IIDX_DAN_RANK_7_DAN - DAN_RANK_8_DAN = DBConstants.IIDX_DAN_RANK_8_DAN - DAN_RANK_9_DAN = DBConstants.IIDX_DAN_RANK_9_DAN - DAN_RANK_10_DAN = DBConstants.IIDX_DAN_RANK_10_DAN - DAN_RANK_CHUDEN = DBConstants.IIDX_DAN_RANK_CHUDEN - DAN_RANK_KAIDEN = DBConstants.IIDX_DAN_RANK_KAIDEN + DAN_RANK_7_KYU: Final[int] = DBConstants.IIDX_DAN_RANK_7_KYU + DAN_RANK_6_KYU: Final[int] = DBConstants.IIDX_DAN_RANK_6_KYU + DAN_RANK_5_KYU: Final[int] = DBConstants.IIDX_DAN_RANK_5_KYU + DAN_RANK_4_KYU: Final[int] = DBConstants.IIDX_DAN_RANK_4_KYU + DAN_RANK_3_KYU: Final[int] = DBConstants.IIDX_DAN_RANK_3_KYU + DAN_RANK_2_KYU: Final[int] = DBConstants.IIDX_DAN_RANK_2_KYU + DAN_RANK_1_KYU: Final[int] = DBConstants.IIDX_DAN_RANK_1_KYU + DAN_RANK_1_DAN: Final[int] = DBConstants.IIDX_DAN_RANK_1_DAN + DAN_RANK_2_DAN: Final[int] = DBConstants.IIDX_DAN_RANK_2_DAN + DAN_RANK_3_DAN: Final[int] = DBConstants.IIDX_DAN_RANK_3_DAN + DAN_RANK_4_DAN: Final[int] = DBConstants.IIDX_DAN_RANK_4_DAN + DAN_RANK_5_DAN: Final[int] = DBConstants.IIDX_DAN_RANK_5_DAN + DAN_RANK_6_DAN: Final[int] = DBConstants.IIDX_DAN_RANK_6_DAN + DAN_RANK_7_DAN: Final[int] = DBConstants.IIDX_DAN_RANK_7_DAN + DAN_RANK_8_DAN: Final[int] = DBConstants.IIDX_DAN_RANK_8_DAN + DAN_RANK_9_DAN: Final[int] = DBConstants.IIDX_DAN_RANK_9_DAN + DAN_RANK_10_DAN: Final[int] = DBConstants.IIDX_DAN_RANK_10_DAN + DAN_RANK_CHUDEN: Final[int] = DBConstants.IIDX_DAN_RANK_CHUDEN + DAN_RANK_KAIDEN: Final[int] = DBConstants.IIDX_DAN_RANK_KAIDEN - DAN_RANKING_SINGLE = 'sgrade' - DAN_RANKING_DOUBLE = 'dgrade' + DAN_RANKING_SINGLE: Final[str] = 'sgrade' + DAN_RANKING_DOUBLE: Final[str] = 'dgrade' - GHOST_TYPE_NONE = 0 - GHOST_TYPE_RIVAL = 100 - GHOST_TYPE_GLOBAL_TOP = 200 - GHOST_TYPE_GLOBAL_AVERAGE = 300 - GHOST_TYPE_LOCAL_TOP = 400 - GHOST_TYPE_LOCAL_AVERAGE = 500 - GHOST_TYPE_DAN_TOP = 600 - GHOST_TYPE_DAN_AVERAGE = 700 - GHOST_TYPE_RIVAL_TOP = 800 - GHOST_TYPE_RIVAL_AVERAGE = 900 + GHOST_TYPE_NONE: Final[int] = 0 + GHOST_TYPE_RIVAL: Final[int] = 100 + GHOST_TYPE_GLOBAL_TOP: Final[int] = 200 + GHOST_TYPE_GLOBAL_AVERAGE: Final[int] = 300 + GHOST_TYPE_LOCAL_TOP: Final[int] = 400 + GHOST_TYPE_LOCAL_AVERAGE: Final[int] = 500 + GHOST_TYPE_DAN_TOP: Final[int] = 600 + GHOST_TYPE_DAN_AVERAGE: Final[int] = 700 + GHOST_TYPE_RIVAL_TOP: Final[int] = 800 + GHOST_TYPE_RIVAL_AVERAGE: Final[int] = 900 # Return the local2 service so that Copula and above will send certain packets. extra_services: List[str] = [ diff --git a/bemani/backend/iidx/bistrover.py b/bemani/backend/iidx/bistrover.py index 21f55ce..98edd6f 100644 --- a/bemani/backend/iidx/bistrover.py +++ b/bemani/backend/iidx/bistrover.py @@ -8,10 +8,10 @@ from bemani.common import VersionConstants class IIDXBistrover(IIDXBase): - name = 'Beatmania IIDX BISTROVER' - version = VersionConstants.IIDX_BISTROVER + name: str = 'Beatmania IIDX BISTROVER' + version: int = VersionConstants.IIDX_BISTROVER - requires_extended_regions = True + requires_extended_regions: bool = True def previous_version(self) -> Optional[IIDXBase]: return IIDXHeroicVerse(self.data, self.config, self.model) diff --git a/bemani/backend/iidx/cannonballers.py b/bemani/backend/iidx/cannonballers.py index 0a5d276..6b591b8 100644 --- a/bemani/backend/iidx/cannonballers.py +++ b/bemani/backend/iidx/cannonballers.py @@ -1,8 +1,8 @@ # vim: set fileencoding=utf-8 -import copy import random import struct from typing import Optional, Dict, Any, List, Tuple +from typing_extensions import Final from bemani.backend.iidx.base import IIDXBase from bemani.backend.iidx.course import IIDXCourse @@ -15,76 +15,76 @@ from bemani.protocol import Node class IIDXCannonBallers(IIDXCourse, IIDXBase): - name = 'Beatmania IIDX CANNON BALLERS' - version = VersionConstants.IIDX_CANNON_BALLERS + name: str = 'Beatmania IIDX CANNON BALLERS' + version: int = VersionConstants.IIDX_CANNON_BALLERS - GAME_CLTYPE_SINGLE = 0 - GAME_CLTYPE_DOUBLE = 1 + GAME_CLTYPE_SINGLE: Final[int] = 0 + GAME_CLTYPE_DOUBLE: Final[int] = 1 - DAN_STAGES = 4 + DAN_STAGES: Final[int] = 4 - GAME_CLEAR_STATUS_NO_PLAY = 0 - GAME_CLEAR_STATUS_FAILED = 1 - GAME_CLEAR_STATUS_ASSIST_CLEAR = 2 - GAME_CLEAR_STATUS_EASY_CLEAR = 3 - GAME_CLEAR_STATUS_CLEAR = 4 - GAME_CLEAR_STATUS_HARD_CLEAR = 5 - GAME_CLEAR_STATUS_EX_HARD_CLEAR = 6 - GAME_CLEAR_STATUS_FULL_COMBO = 7 + GAME_CLEAR_STATUS_NO_PLAY: Final[int] = 0 + GAME_CLEAR_STATUS_FAILED: Final[int] = 1 + GAME_CLEAR_STATUS_ASSIST_CLEAR: Final[int] = 2 + GAME_CLEAR_STATUS_EASY_CLEAR: Final[int] = 3 + GAME_CLEAR_STATUS_CLEAR: Final[int] = 4 + GAME_CLEAR_STATUS_HARD_CLEAR: Final[int] = 5 + GAME_CLEAR_STATUS_EX_HARD_CLEAR: Final[int] = 6 + GAME_CLEAR_STATUS_FULL_COMBO: Final[int] = 7 - GAME_GHOST_TYPE_RIVAL = 1 - GAME_GHOST_TYPE_GLOBAL_TOP = 2 - GAME_GHOST_TYPE_GLOBAL_AVERAGE = 3 - GAME_GHOST_TYPE_LOCAL_TOP = 4 - GAME_GHOST_TYPE_LOCAL_AVERAGE = 5 - GAME_GHOST_TYPE_DAN_TOP = 6 - GAME_GHOST_TYPE_DAN_AVERAGE = 7 - GAME_GHOST_TYPE_RIVAL_TOP = 8 - GAME_GHOST_TYPE_RIVAL_AVERAGE = 9 + GAME_GHOST_TYPE_RIVAL: Final[int] = 1 + GAME_GHOST_TYPE_GLOBAL_TOP: Final[int] = 2 + GAME_GHOST_TYPE_GLOBAL_AVERAGE: Final[int] = 3 + GAME_GHOST_TYPE_LOCAL_TOP: Final[int] = 4 + GAME_GHOST_TYPE_LOCAL_AVERAGE: Final[int] = 5 + GAME_GHOST_TYPE_DAN_TOP: Final[int] = 6 + GAME_GHOST_TYPE_DAN_AVERAGE: Final[int] = 7 + GAME_GHOST_TYPE_RIVAL_TOP: Final[int] = 8 + GAME_GHOST_TYPE_RIVAL_AVERAGE: Final[int] = 9 - GAME_GHOST_LENGTH = 64 + GAME_GHOST_LENGTH: Final[int] = 64 - GAME_SP_DAN_RANK_7_KYU = 0 - GAME_SP_DAN_RANK_6_KYU = 1 - GAME_SP_DAN_RANK_5_KYU = 2 - GAME_SP_DAN_RANK_4_KYU = 3 - GAME_SP_DAN_RANK_3_KYU = 4 - GAME_SP_DAN_RANK_2_KYU = 5 - GAME_SP_DAN_RANK_1_KYU = 6 - GAME_SP_DAN_RANK_1_DAN = 7 - GAME_SP_DAN_RANK_2_DAN = 8 - GAME_SP_DAN_RANK_3_DAN = 9 - GAME_SP_DAN_RANK_4_DAN = 10 - GAME_SP_DAN_RANK_5_DAN = 11 - GAME_SP_DAN_RANK_6_DAN = 12 - GAME_SP_DAN_RANK_7_DAN = 13 - GAME_SP_DAN_RANK_8_DAN = 14 - GAME_SP_DAN_RANK_9_DAN = 15 - GAME_SP_DAN_RANK_10_DAN = 16 - GAME_SP_DAN_RANK_CHUDEN = 17 - GAME_SP_DAN_RANK_KAIDEN = 18 + GAME_SP_DAN_RANK_7_KYU: Final[int] = 0 + GAME_SP_DAN_RANK_6_KYU: Final[int] = 1 + GAME_SP_DAN_RANK_5_KYU: Final[int] = 2 + GAME_SP_DAN_RANK_4_KYU: Final[int] = 3 + GAME_SP_DAN_RANK_3_KYU: Final[int] = 4 + GAME_SP_DAN_RANK_2_KYU: Final[int] = 5 + GAME_SP_DAN_RANK_1_KYU: Final[int] = 6 + GAME_SP_DAN_RANK_1_DAN: Final[int] = 7 + GAME_SP_DAN_RANK_2_DAN: Final[int] = 8 + GAME_SP_DAN_RANK_3_DAN: Final[int] = 9 + GAME_SP_DAN_RANK_4_DAN: Final[int] = 10 + GAME_SP_DAN_RANK_5_DAN: Final[int] = 11 + GAME_SP_DAN_RANK_6_DAN: Final[int] = 12 + GAME_SP_DAN_RANK_7_DAN: Final[int] = 13 + GAME_SP_DAN_RANK_8_DAN: Final[int] = 14 + GAME_SP_DAN_RANK_9_DAN: Final[int] = 15 + GAME_SP_DAN_RANK_10_DAN: Final[int] = 16 + GAME_SP_DAN_RANK_CHUDEN: Final[int] = 17 + GAME_SP_DAN_RANK_KAIDEN: Final[int] = 18 - GAME_DP_DAN_RANK_7_KYU = 0 - GAME_DP_DAN_RANK_6_KYU = 1 - GAME_DP_DAN_RANK_5_KYU = 2 - GAME_DP_DAN_RANK_4_KYU = 3 - GAME_DP_DAN_RANK_3_KYU = 4 - GAME_DP_DAN_RANK_2_KYU = 5 - GAME_DP_DAN_RANK_1_KYU = 6 - GAME_DP_DAN_RANK_1_DAN = 7 - GAME_DP_DAN_RANK_2_DAN = 8 - GAME_DP_DAN_RANK_3_DAN = 9 - GAME_DP_DAN_RANK_4_DAN = 10 - GAME_DP_DAN_RANK_5_DAN = 11 - GAME_DP_DAN_RANK_6_DAN = 12 - GAME_DP_DAN_RANK_7_DAN = 13 - GAME_DP_DAN_RANK_8_DAN = 14 - GAME_DP_DAN_RANK_9_DAN = 15 - GAME_DP_DAN_RANK_10_DAN = 16 - GAME_DP_DAN_RANK_CHUDEN = 17 - GAME_DP_DAN_RANK_KAIDEN = 18 + GAME_DP_DAN_RANK_7_KYU: Final[int] = 0 + GAME_DP_DAN_RANK_6_KYU: Final[int] = 1 + GAME_DP_DAN_RANK_5_KYU: Final[int] = 2 + GAME_DP_DAN_RANK_4_KYU: Final[int] = 3 + GAME_DP_DAN_RANK_3_KYU: Final[int] = 4 + GAME_DP_DAN_RANK_2_KYU: Final[int] = 5 + GAME_DP_DAN_RANK_1_KYU: Final[int] = 6 + GAME_DP_DAN_RANK_1_DAN: Final[int] = 7 + GAME_DP_DAN_RANK_2_DAN: Final[int] = 8 + GAME_DP_DAN_RANK_3_DAN: Final[int] = 9 + GAME_DP_DAN_RANK_4_DAN: Final[int] = 10 + GAME_DP_DAN_RANK_5_DAN: Final[int] = 11 + GAME_DP_DAN_RANK_6_DAN: Final[int] = 12 + GAME_DP_DAN_RANK_7_DAN: Final[int] = 13 + GAME_DP_DAN_RANK_8_DAN: Final[int] = 14 + GAME_DP_DAN_RANK_9_DAN: Final[int] = 15 + GAME_DP_DAN_RANK_10_DAN: Final[int] = 16 + GAME_DP_DAN_RANK_CHUDEN: Final[int] = 17 + GAME_DP_DAN_RANK_KAIDEN: Final[int] = 18 - FAVORITE_LIST_LENGTH = 20 + FAVORITE_LIST_LENGTH: Final[int] = 20 requires_extended_regions: bool = True @@ -1865,7 +1865,7 @@ class IIDXCannonBallers(IIDXCourse, IIDXBase): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() play_stats = self.get_play_statistics(userid) # Track play counts diff --git a/bemani/backend/iidx/copula.py b/bemani/backend/iidx/copula.py index 342103a..b3a64d3 100644 --- a/bemani/backend/iidx/copula.py +++ b/bemani/backend/iidx/copula.py @@ -1,8 +1,8 @@ # vim: set fileencoding=utf-8 -import copy import random import struct from typing import Optional, Dict, Any, List, Tuple +from typing_extensions import Final from bemani.backend.iidx.base import IIDXBase from bemani.backend.iidx.course import IIDXCourse @@ -15,76 +15,76 @@ from bemani.protocol import Node class IIDXCopula(IIDXCourse, IIDXBase): - name = 'Beatmania IIDX copula' - version = VersionConstants.IIDX_COPULA + name: str = 'Beatmania IIDX copula' + version: int = VersionConstants.IIDX_COPULA - GAME_CLTYPE_SINGLE = 0 - GAME_CLTYPE_DOUBLE = 1 + GAME_CLTYPE_SINGLE: Final[int] = 0 + GAME_CLTYPE_DOUBLE: Final[int] = 1 - DAN_STAGES = 4 + DAN_STAGES: Final[int] = 4 - GAME_CLEAR_STATUS_NO_PLAY = 0 - GAME_CLEAR_STATUS_FAILED = 1 - GAME_CLEAR_STATUS_ASSIST_CLEAR = 2 - GAME_CLEAR_STATUS_EASY_CLEAR = 3 - GAME_CLEAR_STATUS_CLEAR = 4 - GAME_CLEAR_STATUS_HARD_CLEAR = 5 - GAME_CLEAR_STATUS_EX_HARD_CLEAR = 6 - GAME_CLEAR_STATUS_FULL_COMBO = 7 + GAME_CLEAR_STATUS_NO_PLAY: Final[int] = 0 + GAME_CLEAR_STATUS_FAILED: Final[int] = 1 + GAME_CLEAR_STATUS_ASSIST_CLEAR: Final[int] = 2 + GAME_CLEAR_STATUS_EASY_CLEAR: Final[int] = 3 + GAME_CLEAR_STATUS_CLEAR: Final[int] = 4 + GAME_CLEAR_STATUS_HARD_CLEAR: Final[int] = 5 + GAME_CLEAR_STATUS_EX_HARD_CLEAR: Final[int] = 6 + GAME_CLEAR_STATUS_FULL_COMBO: Final[int] = 7 - GAME_GHOST_TYPE_RIVAL = 1 - GAME_GHOST_TYPE_GLOBAL_TOP = 2 - GAME_GHOST_TYPE_GLOBAL_AVERAGE = 3 - GAME_GHOST_TYPE_LOCAL_TOP = 4 - GAME_GHOST_TYPE_LOCAL_AVERAGE = 5 - GAME_GHOST_TYPE_DAN_TOP = 6 - GAME_GHOST_TYPE_DAN_AVERAGE = 7 - GAME_GHOST_TYPE_RIVAL_TOP = 8 - GAME_GHOST_TYPE_RIVAL_AVERAGE = 9 + GAME_GHOST_TYPE_RIVAL: Final[int] = 1 + GAME_GHOST_TYPE_GLOBAL_TOP: Final[int] = 2 + GAME_GHOST_TYPE_GLOBAL_AVERAGE: Final[int] = 3 + GAME_GHOST_TYPE_LOCAL_TOP: Final[int] = 4 + GAME_GHOST_TYPE_LOCAL_AVERAGE: Final[int] = 5 + GAME_GHOST_TYPE_DAN_TOP: Final[int] = 6 + GAME_GHOST_TYPE_DAN_AVERAGE: Final[int] = 7 + GAME_GHOST_TYPE_RIVAL_TOP: Final[int] = 8 + GAME_GHOST_TYPE_RIVAL_AVERAGE: Final[int] = 9 - GAME_GHOST_LENGTH = 64 + GAME_GHOST_LENGTH: Final[int] = 64 - GAME_SP_DAN_RANK_7_KYU = 0 - GAME_SP_DAN_RANK_6_KYU = 1 - GAME_SP_DAN_RANK_5_KYU = 2 - GAME_SP_DAN_RANK_4_KYU = 3 - GAME_SP_DAN_RANK_3_KYU = 4 - GAME_SP_DAN_RANK_2_KYU = 5 - GAME_SP_DAN_RANK_1_KYU = 6 - GAME_SP_DAN_RANK_1_DAN = 7 - GAME_SP_DAN_RANK_2_DAN = 8 - GAME_SP_DAN_RANK_3_DAN = 9 - GAME_SP_DAN_RANK_4_DAN = 10 - GAME_SP_DAN_RANK_5_DAN = 11 - GAME_SP_DAN_RANK_6_DAN = 12 - GAME_SP_DAN_RANK_7_DAN = 13 - GAME_SP_DAN_RANK_8_DAN = 14 - GAME_SP_DAN_RANK_9_DAN = 15 - GAME_SP_DAN_RANK_10_DAN = 16 - GAME_SP_DAN_RANK_CHUDEN = 17 - GAME_SP_DAN_RANK_KAIDEN = 18 + GAME_SP_DAN_RANK_7_KYU: Final[int] = 0 + GAME_SP_DAN_RANK_6_KYU: Final[int] = 1 + GAME_SP_DAN_RANK_5_KYU: Final[int] = 2 + GAME_SP_DAN_RANK_4_KYU: Final[int] = 3 + GAME_SP_DAN_RANK_3_KYU: Final[int] = 4 + GAME_SP_DAN_RANK_2_KYU: Final[int] = 5 + GAME_SP_DAN_RANK_1_KYU: Final[int] = 6 + GAME_SP_DAN_RANK_1_DAN: Final[int] = 7 + GAME_SP_DAN_RANK_2_DAN: Final[int] = 8 + GAME_SP_DAN_RANK_3_DAN: Final[int] = 9 + GAME_SP_DAN_RANK_4_DAN: Final[int] = 10 + GAME_SP_DAN_RANK_5_DAN: Final[int] = 11 + GAME_SP_DAN_RANK_6_DAN: Final[int] = 12 + GAME_SP_DAN_RANK_7_DAN: Final[int] = 13 + GAME_SP_DAN_RANK_8_DAN: Final[int] = 14 + GAME_SP_DAN_RANK_9_DAN: Final[int] = 15 + GAME_SP_DAN_RANK_10_DAN: Final[int] = 16 + GAME_SP_DAN_RANK_CHUDEN: Final[int] = 17 + GAME_SP_DAN_RANK_KAIDEN: Final[int] = 18 - GAME_DP_DAN_RANK_7_KYU = 0 - GAME_DP_DAN_RANK_6_KYU = 1 - GAME_DP_DAN_RANK_5_KYU = 2 - GAME_DP_DAN_RANK_4_KYU = 3 - GAME_DP_DAN_RANK_3_KYU = 4 - GAME_DP_DAN_RANK_2_KYU = 5 - GAME_DP_DAN_RANK_1_KYU = 6 - GAME_DP_DAN_RANK_1_DAN = 7 - GAME_DP_DAN_RANK_2_DAN = 8 - GAME_DP_DAN_RANK_3_DAN = 9 - GAME_DP_DAN_RANK_4_DAN = 10 - GAME_DP_DAN_RANK_5_DAN = 11 - GAME_DP_DAN_RANK_6_DAN = 12 - GAME_DP_DAN_RANK_7_DAN = 13 - GAME_DP_DAN_RANK_8_DAN = 14 - GAME_DP_DAN_RANK_9_DAN = 15 - GAME_DP_DAN_RANK_10_DAN = 16 - GAME_DP_DAN_RANK_CHUDEN = 17 - GAME_DP_DAN_RANK_KAIDEN = 18 + GAME_DP_DAN_RANK_7_KYU: Final[int] = 0 + GAME_DP_DAN_RANK_6_KYU: Final[int] = 1 + GAME_DP_DAN_RANK_5_KYU: Final[int] = 2 + GAME_DP_DAN_RANK_4_KYU: Final[int] = 3 + GAME_DP_DAN_RANK_3_KYU: Final[int] = 4 + GAME_DP_DAN_RANK_2_KYU: Final[int] = 5 + GAME_DP_DAN_RANK_1_KYU: Final[int] = 6 + GAME_DP_DAN_RANK_1_DAN: Final[int] = 7 + GAME_DP_DAN_RANK_2_DAN: Final[int] = 8 + GAME_DP_DAN_RANK_3_DAN: Final[int] = 9 + GAME_DP_DAN_RANK_4_DAN: Final[int] = 10 + GAME_DP_DAN_RANK_5_DAN: Final[int] = 11 + GAME_DP_DAN_RANK_6_DAN: Final[int] = 12 + GAME_DP_DAN_RANK_7_DAN: Final[int] = 13 + GAME_DP_DAN_RANK_8_DAN: Final[int] = 14 + GAME_DP_DAN_RANK_9_DAN: Final[int] = 15 + GAME_DP_DAN_RANK_10_DAN: Final[int] = 16 + GAME_DP_DAN_RANK_CHUDEN: Final[int] = 17 + GAME_DP_DAN_RANK_KAIDEN: Final[int] = 18 - FAVORITE_LIST_LENGTH = 20 + FAVORITE_LIST_LENGTH: Final[int] = 20 def previous_version(self) -> Optional[IIDXBase]: return IIDXPendual(self.data, self.config, self.model) @@ -1872,7 +1872,7 @@ class IIDXCopula(IIDXCourse, IIDXBase): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() play_stats = self.get_play_statistics(userid) # Track play counts diff --git a/bemani/backend/iidx/course.py b/bemani/backend/iidx/course.py index 39a6dee..3df26f4 100644 --- a/bemani/backend/iidx/course.py +++ b/bemani/backend/iidx/course.py @@ -1,5 +1,6 @@ # vim: set fileencoding=utf-8 from typing import Tuple +from typing_extensions import Final from bemani.backend.iidx.base import IIDXBase from bemani.common import ValidatedDict @@ -8,9 +9,9 @@ from bemani.data import UserID class IIDXCourse(IIDXBase): - COURSE_TYPE_SECRET = 'secret_course' - COURSE_TYPE_INTERNET_RANKING = 'ir_course' - COURSE_TYPE_CLASSIC = 'classic_course' + COURSE_TYPE_SECRET: Final[str] = 'secret_course' + COURSE_TYPE_INTERNET_RANKING: Final[str] = 'ir_course' + COURSE_TYPE_CLASSIC: Final[str] = 'classic_course' def id_and_chart_from_courseid(self, courseid: int) -> Tuple[int, int]: return (int(courseid / 6), courseid % 6) diff --git a/bemani/backend/iidx/factory.py b/bemani/backend/iidx/factory.py index 44536ac..ef3eb42 100644 --- a/bemani/backend/iidx/factory.py +++ b/bemani/backend/iidx/factory.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import List, Optional, Type from bemani.backend.base import Base, Factory from bemani.backend.iidx.stubs import ( @@ -37,7 +37,7 @@ from bemani.data import Config, Data class IIDXFactory(Factory): - MANAGED_CLASSES = [ + MANAGED_CLASSES: List[Type[Base]] = [ IIDX1stStyle, IIDX2ndStyle, IIDX3rdStyle, diff --git a/bemani/backend/iidx/heroicverse.py b/bemani/backend/iidx/heroicverse.py index c84c3fc..bcb1409 100644 --- a/bemani/backend/iidx/heroicverse.py +++ b/bemani/backend/iidx/heroicverse.py @@ -8,10 +8,10 @@ from bemani.common import VersionConstants class IIDXHeroicVerse(IIDXBase): - name = 'Beatmania IIDX HEROIC VERSE' - version = VersionConstants.IIDX_HEROIC_VERSE + name: str = 'Beatmania IIDX HEROIC VERSE' + version: int = VersionConstants.IIDX_HEROIC_VERSE - requires_extended_regions = True + requires_extended_regions: bool = True def previous_version(self) -> Optional[IIDXBase]: return IIDXRootage(self.data, self.config, self.model) diff --git a/bemani/backend/iidx/pendual.py b/bemani/backend/iidx/pendual.py index 86f8d80..f69cdc7 100644 --- a/bemani/backend/iidx/pendual.py +++ b/bemani/backend/iidx/pendual.py @@ -1,8 +1,8 @@ # vim: set fileencoding=utf-8 -import copy import random import struct from typing import Optional, Dict, Any, List, Tuple +from typing_extensions import Final from bemani.backend.iidx.base import IIDXBase from bemani.backend.iidx.course import IIDXCourse @@ -15,73 +15,73 @@ from bemani.protocol import Node class IIDXPendual(IIDXCourse, IIDXBase): - name = 'Beatmania IIDX PENDUAL' - version = VersionConstants.IIDX_PENDUAL + name: str = 'Beatmania IIDX PENDUAL' + version: int = VersionConstants.IIDX_PENDUAL - GAME_CLTYPE_SINGLE = 0 - GAME_CLTYPE_DOUBLE = 1 + GAME_CLTYPE_SINGLE: Final[int] = 0 + GAME_CLTYPE_DOUBLE: Final[int] = 1 - DAN_STAGES_SINGLE = 4 - DAN_STAGES_DOUBLE = 3 + DAN_STAGES_SINGLE: Final[int] = 4 + DAN_STAGES_DOUBLE: Final[int] = 3 - GAME_CLEAR_STATUS_NO_PLAY = 0 - GAME_CLEAR_STATUS_FAILED = 1 - GAME_CLEAR_STATUS_ASSIST_CLEAR = 2 - GAME_CLEAR_STATUS_EASY_CLEAR = 3 - GAME_CLEAR_STATUS_CLEAR = 4 - GAME_CLEAR_STATUS_HARD_CLEAR = 5 - GAME_CLEAR_STATUS_EX_HARD_CLEAR = 6 - GAME_CLEAR_STATUS_FULL_COMBO = 7 + GAME_CLEAR_STATUS_NO_PLAY: Final[int] = 0 + GAME_CLEAR_STATUS_FAILED: Final[int] = 1 + GAME_CLEAR_STATUS_ASSIST_CLEAR: Final[int] = 2 + GAME_CLEAR_STATUS_EASY_CLEAR: Final[int] = 3 + GAME_CLEAR_STATUS_CLEAR: Final[int] = 4 + GAME_CLEAR_STATUS_HARD_CLEAR: Final[int] = 5 + GAME_CLEAR_STATUS_EX_HARD_CLEAR: Final[int] = 6 + GAME_CLEAR_STATUS_FULL_COMBO: Final[int] = 7 - GAME_GHOST_TYPE_RIVAL = 1 - GAME_GHOST_TYPE_GLOBAL_TOP = 2 - GAME_GHOST_TYPE_GLOBAL_AVERAGE = 3 - GAME_GHOST_TYPE_LOCAL_TOP = 4 - GAME_GHOST_TYPE_LOCAL_AVERAGE = 5 - GAME_GHOST_TYPE_DAN_TOP = 6 - GAME_GHOST_TYPE_DAN_AVERAGE = 7 - GAME_GHOST_TYPE_RIVAL_TOP = 8 - GAME_GHOST_TYPE_RIVAL_AVERAGE = 9 + GAME_GHOST_TYPE_RIVAL: Final[int] = 1 + GAME_GHOST_TYPE_GLOBAL_TOP: Final[int] = 2 + GAME_GHOST_TYPE_GLOBAL_AVERAGE: Final[int] = 3 + GAME_GHOST_TYPE_LOCAL_TOP: Final[int] = 4 + GAME_GHOST_TYPE_LOCAL_AVERAGE: Final[int] = 5 + GAME_GHOST_TYPE_DAN_TOP: Final[int] = 6 + GAME_GHOST_TYPE_DAN_AVERAGE: Final[int] = 7 + GAME_GHOST_TYPE_RIVAL_TOP: Final[int] = 8 + GAME_GHOST_TYPE_RIVAL_AVERAGE: Final[int] = 9 - GAME_GHOST_LENGTH = 64 + GAME_GHOST_LENGTH: Final[int] = 64 - GAME_SP_DAN_RANK_7_KYU = 0 - GAME_SP_DAN_RANK_6_KYU = 1 - GAME_SP_DAN_RANK_5_KYU = 2 - GAME_SP_DAN_RANK_4_KYU = 3 - GAME_SP_DAN_RANK_3_KYU = 4 - GAME_SP_DAN_RANK_2_KYU = 5 - GAME_SP_DAN_RANK_1_KYU = 6 - GAME_SP_DAN_RANK_1_DAN = 7 - GAME_SP_DAN_RANK_2_DAN = 8 - GAME_SP_DAN_RANK_3_DAN = 9 - GAME_SP_DAN_RANK_4_DAN = 10 - GAME_SP_DAN_RANK_5_DAN = 11 - GAME_SP_DAN_RANK_6_DAN = 12 - GAME_SP_DAN_RANK_7_DAN = 13 - GAME_SP_DAN_RANK_8_DAN = 14 - GAME_SP_DAN_RANK_9_DAN = 15 - GAME_SP_DAN_RANK_10_DAN = 16 - GAME_SP_DAN_RANK_KAIDEN = 17 + GAME_SP_DAN_RANK_7_KYU: Final[int] = 0 + GAME_SP_DAN_RANK_6_KYU: Final[int] = 1 + GAME_SP_DAN_RANK_5_KYU: Final[int] = 2 + GAME_SP_DAN_RANK_4_KYU: Final[int] = 3 + GAME_SP_DAN_RANK_3_KYU: Final[int] = 4 + GAME_SP_DAN_RANK_2_KYU: Final[int] = 5 + GAME_SP_DAN_RANK_1_KYU: Final[int] = 6 + GAME_SP_DAN_RANK_1_DAN: Final[int] = 7 + GAME_SP_DAN_RANK_2_DAN: Final[int] = 8 + GAME_SP_DAN_RANK_3_DAN: Final[int] = 9 + GAME_SP_DAN_RANK_4_DAN: Final[int] = 10 + GAME_SP_DAN_RANK_5_DAN: Final[int] = 11 + GAME_SP_DAN_RANK_6_DAN: Final[int] = 12 + GAME_SP_DAN_RANK_7_DAN: Final[int] = 13 + GAME_SP_DAN_RANK_8_DAN: Final[int] = 14 + GAME_SP_DAN_RANK_9_DAN: Final[int] = 15 + GAME_SP_DAN_RANK_10_DAN: Final[int] = 16 + GAME_SP_DAN_RANK_KAIDEN: Final[int] = 17 - GAME_DP_DAN_RANK_5_KYU = 0 - GAME_DP_DAN_RANK_4_KYU = 1 - GAME_DP_DAN_RANK_3_KYU = 2 - GAME_DP_DAN_RANK_2_KYU = 3 - GAME_DP_DAN_RANK_1_KYU = 4 - GAME_DP_DAN_RANK_1_DAN = 5 - GAME_DP_DAN_RANK_2_DAN = 6 - GAME_DP_DAN_RANK_3_DAN = 7 - GAME_DP_DAN_RANK_4_DAN = 8 - GAME_DP_DAN_RANK_5_DAN = 9 - GAME_DP_DAN_RANK_6_DAN = 10 - GAME_DP_DAN_RANK_7_DAN = 11 - GAME_DP_DAN_RANK_8_DAN = 12 - GAME_DP_DAN_RANK_9_DAN = 13 - GAME_DP_DAN_RANK_10_DAN = 14 - GAME_DP_DAN_RANK_KAIDEN = 15 + GAME_DP_DAN_RANK_5_KYU: Final[int] = 0 + GAME_DP_DAN_RANK_4_KYU: Final[int] = 1 + GAME_DP_DAN_RANK_3_KYU: Final[int] = 2 + GAME_DP_DAN_RANK_2_KYU: Final[int] = 3 + GAME_DP_DAN_RANK_1_KYU: Final[int] = 4 + GAME_DP_DAN_RANK_1_DAN: Final[int] = 5 + GAME_DP_DAN_RANK_2_DAN: Final[int] = 6 + GAME_DP_DAN_RANK_3_DAN: Final[int] = 7 + GAME_DP_DAN_RANK_4_DAN: Final[int] = 8 + GAME_DP_DAN_RANK_5_DAN: Final[int] = 9 + GAME_DP_DAN_RANK_6_DAN: Final[int] = 10 + GAME_DP_DAN_RANK_7_DAN: Final[int] = 11 + GAME_DP_DAN_RANK_8_DAN: Final[int] = 12 + GAME_DP_DAN_RANK_9_DAN: Final[int] = 13 + GAME_DP_DAN_RANK_10_DAN: Final[int] = 14 + GAME_DP_DAN_RANK_KAIDEN: Final[int] = 15 - FAVORITE_LIST_LENGTH = 20 + FAVORITE_LIST_LENGTH: Final[int] = 20 def previous_version(self) -> Optional[IIDXBase]: return IIDXSpada(self.data, self.config, self.model) @@ -1718,7 +1718,7 @@ class IIDXPendual(IIDXCourse, IIDXBase): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() play_stats = self.get_play_statistics(userid) # Track play counts diff --git a/bemani/backend/iidx/rootage.py b/bemani/backend/iidx/rootage.py index a2b2ab0..2508fc9 100644 --- a/bemani/backend/iidx/rootage.py +++ b/bemani/backend/iidx/rootage.py @@ -1,8 +1,8 @@ # vim: set fileencoding=utf-8 -import copy import random import struct from typing import Optional, Dict, Any, List, Tuple +from typing_extensions import Final from bemani.backend.iidx.base import IIDXBase from bemani.backend.iidx.course import IIDXCourse @@ -15,76 +15,76 @@ from bemani.protocol import Node class IIDXRootage(IIDXCourse, IIDXBase): - name = 'Beatmania IIDX ROOTAGE' - version = VersionConstants.IIDX_ROOTAGE + name: str = 'Beatmania IIDX ROOTAGE' + version: int = VersionConstants.IIDX_ROOTAGE - GAME_CLTYPE_SINGLE = 0 - GAME_CLTYPE_DOUBLE = 1 + GAME_CLTYPE_SINGLE: Final[int] = 0 + GAME_CLTYPE_DOUBLE: Final[int] = 1 - DAN_STAGES = 4 + DAN_STAGES: Final[int] = 4 - GAME_CLEAR_STATUS_NO_PLAY = 0 - GAME_CLEAR_STATUS_FAILED = 1 - GAME_CLEAR_STATUS_ASSIST_CLEAR = 2 - GAME_CLEAR_STATUS_EASY_CLEAR = 3 - GAME_CLEAR_STATUS_CLEAR = 4 - GAME_CLEAR_STATUS_HARD_CLEAR = 5 - GAME_CLEAR_STATUS_EX_HARD_CLEAR = 6 - GAME_CLEAR_STATUS_FULL_COMBO = 7 + GAME_CLEAR_STATUS_NO_PLAY: Final[int] = 0 + GAME_CLEAR_STATUS_FAILED: Final[int] = 1 + GAME_CLEAR_STATUS_ASSIST_CLEAR: Final[int] = 2 + GAME_CLEAR_STATUS_EASY_CLEAR: Final[int] = 3 + GAME_CLEAR_STATUS_CLEAR: Final[int] = 4 + GAME_CLEAR_STATUS_HARD_CLEAR: Final[int] = 5 + GAME_CLEAR_STATUS_EX_HARD_CLEAR: Final[int] = 6 + GAME_CLEAR_STATUS_FULL_COMBO: Final[int] = 7 - GAME_GHOST_TYPE_RIVAL = 1 - GAME_GHOST_TYPE_GLOBAL_TOP = 2 - GAME_GHOST_TYPE_GLOBAL_AVERAGE = 3 - GAME_GHOST_TYPE_LOCAL_TOP = 4 - GAME_GHOST_TYPE_LOCAL_AVERAGE = 5 - GAME_GHOST_TYPE_DAN_TOP = 6 - GAME_GHOST_TYPE_DAN_AVERAGE = 7 - GAME_GHOST_TYPE_RIVAL_TOP = 8 - GAME_GHOST_TYPE_RIVAL_AVERAGE = 9 + GAME_GHOST_TYPE_RIVAL: Final[int] = 1 + GAME_GHOST_TYPE_GLOBAL_TOP: Final[int] = 2 + GAME_GHOST_TYPE_GLOBAL_AVERAGE: Final[int] = 3 + GAME_GHOST_TYPE_LOCAL_TOP: Final[int] = 4 + GAME_GHOST_TYPE_LOCAL_AVERAGE: Final[int] = 5 + GAME_GHOST_TYPE_DAN_TOP: Final[int] = 6 + GAME_GHOST_TYPE_DAN_AVERAGE: Final[int] = 7 + GAME_GHOST_TYPE_RIVAL_TOP: Final[int] = 8 + GAME_GHOST_TYPE_RIVAL_AVERAGE: Final[int] = 9 - GAME_GHOST_LENGTH = 64 + GAME_GHOST_LENGTH: Final[int] = 64 - GAME_SP_DAN_RANK_7_KYU = 0 - GAME_SP_DAN_RANK_6_KYU = 1 - GAME_SP_DAN_RANK_5_KYU = 2 - GAME_SP_DAN_RANK_4_KYU = 3 - GAME_SP_DAN_RANK_3_KYU = 4 - GAME_SP_DAN_RANK_2_KYU = 5 - GAME_SP_DAN_RANK_1_KYU = 6 - GAME_SP_DAN_RANK_1_DAN = 7 - GAME_SP_DAN_RANK_2_DAN = 8 - GAME_SP_DAN_RANK_3_DAN = 9 - GAME_SP_DAN_RANK_4_DAN = 10 - GAME_SP_DAN_RANK_5_DAN = 11 - GAME_SP_DAN_RANK_6_DAN = 12 - GAME_SP_DAN_RANK_7_DAN = 13 - GAME_SP_DAN_RANK_8_DAN = 14 - GAME_SP_DAN_RANK_9_DAN = 15 - GAME_SP_DAN_RANK_10_DAN = 16 - GAME_SP_DAN_RANK_CHUDEN = 17 - GAME_SP_DAN_RANK_KAIDEN = 18 + GAME_SP_DAN_RANK_7_KYU: Final[int] = 0 + GAME_SP_DAN_RANK_6_KYU: Final[int] = 1 + GAME_SP_DAN_RANK_5_KYU: Final[int] = 2 + GAME_SP_DAN_RANK_4_KYU: Final[int] = 3 + GAME_SP_DAN_RANK_3_KYU: Final[int] = 4 + GAME_SP_DAN_RANK_2_KYU: Final[int] = 5 + GAME_SP_DAN_RANK_1_KYU: Final[int] = 6 + GAME_SP_DAN_RANK_1_DAN: Final[int] = 7 + GAME_SP_DAN_RANK_2_DAN: Final[int] = 8 + GAME_SP_DAN_RANK_3_DAN: Final[int] = 9 + GAME_SP_DAN_RANK_4_DAN: Final[int] = 10 + GAME_SP_DAN_RANK_5_DAN: Final[int] = 11 + GAME_SP_DAN_RANK_6_DAN: Final[int] = 12 + GAME_SP_DAN_RANK_7_DAN: Final[int] = 13 + GAME_SP_DAN_RANK_8_DAN: Final[int] = 14 + GAME_SP_DAN_RANK_9_DAN: Final[int] = 15 + GAME_SP_DAN_RANK_10_DAN: Final[int] = 16 + GAME_SP_DAN_RANK_CHUDEN: Final[int] = 17 + GAME_SP_DAN_RANK_KAIDEN: Final[int] = 18 - GAME_DP_DAN_RANK_7_KYU = 0 - GAME_DP_DAN_RANK_6_KYU = 1 - GAME_DP_DAN_RANK_5_KYU = 2 - GAME_DP_DAN_RANK_4_KYU = 3 - GAME_DP_DAN_RANK_3_KYU = 4 - GAME_DP_DAN_RANK_2_KYU = 5 - GAME_DP_DAN_RANK_1_KYU = 6 - GAME_DP_DAN_RANK_1_DAN = 7 - GAME_DP_DAN_RANK_2_DAN = 8 - GAME_DP_DAN_RANK_3_DAN = 9 - GAME_DP_DAN_RANK_4_DAN = 10 - GAME_DP_DAN_RANK_5_DAN = 11 - GAME_DP_DAN_RANK_6_DAN = 12 - GAME_DP_DAN_RANK_7_DAN = 13 - GAME_DP_DAN_RANK_8_DAN = 14 - GAME_DP_DAN_RANK_9_DAN = 15 - GAME_DP_DAN_RANK_10_DAN = 16 - GAME_DP_DAN_RANK_CHUDEN = 17 - GAME_DP_DAN_RANK_KAIDEN = 18 + GAME_DP_DAN_RANK_7_KYU: Final[int] = 0 + GAME_DP_DAN_RANK_6_KYU: Final[int] = 1 + GAME_DP_DAN_RANK_5_KYU: Final[int] = 2 + GAME_DP_DAN_RANK_4_KYU: Final[int] = 3 + GAME_DP_DAN_RANK_3_KYU: Final[int] = 4 + GAME_DP_DAN_RANK_2_KYU: Final[int] = 5 + GAME_DP_DAN_RANK_1_KYU: Final[int] = 6 + GAME_DP_DAN_RANK_1_DAN: Final[int] = 7 + GAME_DP_DAN_RANK_2_DAN: Final[int] = 8 + GAME_DP_DAN_RANK_3_DAN: Final[int] = 9 + GAME_DP_DAN_RANK_4_DAN: Final[int] = 10 + GAME_DP_DAN_RANK_5_DAN: Final[int] = 11 + GAME_DP_DAN_RANK_6_DAN: Final[int] = 12 + GAME_DP_DAN_RANK_7_DAN: Final[int] = 13 + GAME_DP_DAN_RANK_8_DAN: Final[int] = 14 + GAME_DP_DAN_RANK_9_DAN: Final[int] = 15 + GAME_DP_DAN_RANK_10_DAN: Final[int] = 16 + GAME_DP_DAN_RANK_CHUDEN: Final[int] = 17 + GAME_DP_DAN_RANK_KAIDEN: Final[int] = 18 - FAVORITE_LIST_LENGTH = 20 + FAVORITE_LIST_LENGTH: Final[int] = 20 requires_extended_regions = True @@ -1577,7 +1577,7 @@ class IIDXRootage(IIDXCourse, IIDXBase): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() play_stats = self.get_play_statistics(userid) # Track play counts diff --git a/bemani/backend/iidx/sinobuz.py b/bemani/backend/iidx/sinobuz.py index c3932dd..d3b5851 100644 --- a/bemani/backend/iidx/sinobuz.py +++ b/bemani/backend/iidx/sinobuz.py @@ -1,8 +1,8 @@ # vim: set fileencoding=utf-8 -import copy import random import struct from typing import Optional, Dict, Any, List, Tuple +from typing_extensions import Final from bemani.backend.iidx.base import IIDXBase from bemani.backend.iidx.course import IIDXCourse @@ -15,76 +15,76 @@ from bemani.protocol import Node class IIDXSinobuz(IIDXCourse, IIDXBase): - name = 'Beatmania IIDX SINOBUZ' - version = VersionConstants.IIDX_SINOBUZ + name: str = 'Beatmania IIDX SINOBUZ' + version: int = VersionConstants.IIDX_SINOBUZ - GAME_CLTYPE_SINGLE = 0 - GAME_CLTYPE_DOUBLE = 1 + GAME_CLTYPE_SINGLE: Final[int] = 0 + GAME_CLTYPE_DOUBLE: Final[int] = 1 - DAN_STAGES = 4 + DAN_STAGES: Final[int] = 4 - GAME_CLEAR_STATUS_NO_PLAY = 0 - GAME_CLEAR_STATUS_FAILED = 1 - GAME_CLEAR_STATUS_ASSIST_CLEAR = 2 - GAME_CLEAR_STATUS_EASY_CLEAR = 3 - GAME_CLEAR_STATUS_CLEAR = 4 - GAME_CLEAR_STATUS_HARD_CLEAR = 5 - GAME_CLEAR_STATUS_EX_HARD_CLEAR = 6 - GAME_CLEAR_STATUS_FULL_COMBO = 7 + GAME_CLEAR_STATUS_NO_PLAY: Final[int] = 0 + GAME_CLEAR_STATUS_FAILED: Final[int] = 1 + GAME_CLEAR_STATUS_ASSIST_CLEAR: Final[int] = 2 + GAME_CLEAR_STATUS_EASY_CLEAR: Final[int] = 3 + GAME_CLEAR_STATUS_CLEAR: Final[int] = 4 + GAME_CLEAR_STATUS_HARD_CLEAR: Final[int] = 5 + GAME_CLEAR_STATUS_EX_HARD_CLEAR: Final[int] = 6 + GAME_CLEAR_STATUS_FULL_COMBO: Final[int] = 7 - GAME_GHOST_TYPE_RIVAL = 1 - GAME_GHOST_TYPE_GLOBAL_TOP = 2 - GAME_GHOST_TYPE_GLOBAL_AVERAGE = 3 - GAME_GHOST_TYPE_LOCAL_TOP = 4 - GAME_GHOST_TYPE_LOCAL_AVERAGE = 5 - GAME_GHOST_TYPE_DAN_TOP = 6 - GAME_GHOST_TYPE_DAN_AVERAGE = 7 - GAME_GHOST_TYPE_RIVAL_TOP = 8 - GAME_GHOST_TYPE_RIVAL_AVERAGE = 9 + GAME_GHOST_TYPE_RIVAL: Final[int] = 1 + GAME_GHOST_TYPE_GLOBAL_TOP: Final[int] = 2 + GAME_GHOST_TYPE_GLOBAL_AVERAGE: Final[int] = 3 + GAME_GHOST_TYPE_LOCAL_TOP: Final[int] = 4 + GAME_GHOST_TYPE_LOCAL_AVERAGE: Final[int] = 5 + GAME_GHOST_TYPE_DAN_TOP: Final[int] = 6 + GAME_GHOST_TYPE_DAN_AVERAGE: Final[int] = 7 + GAME_GHOST_TYPE_RIVAL_TOP: Final[int] = 8 + GAME_GHOST_TYPE_RIVAL_AVERAGE: Final[int] = 9 - GAME_GHOST_LENGTH = 64 + GAME_GHOST_LENGTH: Final[int] = 64 - GAME_SP_DAN_RANK_7_KYU = 0 - GAME_SP_DAN_RANK_6_KYU = 1 - GAME_SP_DAN_RANK_5_KYU = 2 - GAME_SP_DAN_RANK_4_KYU = 3 - GAME_SP_DAN_RANK_3_KYU = 4 - GAME_SP_DAN_RANK_2_KYU = 5 - GAME_SP_DAN_RANK_1_KYU = 6 - GAME_SP_DAN_RANK_1_DAN = 7 - GAME_SP_DAN_RANK_2_DAN = 8 - GAME_SP_DAN_RANK_3_DAN = 9 - GAME_SP_DAN_RANK_4_DAN = 10 - GAME_SP_DAN_RANK_5_DAN = 11 - GAME_SP_DAN_RANK_6_DAN = 12 - GAME_SP_DAN_RANK_7_DAN = 13 - GAME_SP_DAN_RANK_8_DAN = 14 - GAME_SP_DAN_RANK_9_DAN = 15 - GAME_SP_DAN_RANK_10_DAN = 16 - GAME_SP_DAN_RANK_CHUDEN = 17 - GAME_SP_DAN_RANK_KAIDEN = 18 + GAME_SP_DAN_RANK_7_KYU: Final[int] = 0 + GAME_SP_DAN_RANK_6_KYU: Final[int] = 1 + GAME_SP_DAN_RANK_5_KYU: Final[int] = 2 + GAME_SP_DAN_RANK_4_KYU: Final[int] = 3 + GAME_SP_DAN_RANK_3_KYU: Final[int] = 4 + GAME_SP_DAN_RANK_2_KYU: Final[int] = 5 + GAME_SP_DAN_RANK_1_KYU: Final[int] = 6 + GAME_SP_DAN_RANK_1_DAN: Final[int] = 7 + GAME_SP_DAN_RANK_2_DAN: Final[int] = 8 + GAME_SP_DAN_RANK_3_DAN: Final[int] = 9 + GAME_SP_DAN_RANK_4_DAN: Final[int] = 10 + GAME_SP_DAN_RANK_5_DAN: Final[int] = 11 + GAME_SP_DAN_RANK_6_DAN: Final[int] = 12 + GAME_SP_DAN_RANK_7_DAN: Final[int] = 13 + GAME_SP_DAN_RANK_8_DAN: Final[int] = 14 + GAME_SP_DAN_RANK_9_DAN: Final[int] = 15 + GAME_SP_DAN_RANK_10_DAN: Final[int] = 16 + GAME_SP_DAN_RANK_CHUDEN: Final[int] = 17 + GAME_SP_DAN_RANK_KAIDEN: Final[int] = 18 - GAME_DP_DAN_RANK_7_KYU = 0 - GAME_DP_DAN_RANK_6_KYU = 1 - GAME_DP_DAN_RANK_5_KYU = 2 - GAME_DP_DAN_RANK_4_KYU = 3 - GAME_DP_DAN_RANK_3_KYU = 4 - GAME_DP_DAN_RANK_2_KYU = 5 - GAME_DP_DAN_RANK_1_KYU = 6 - GAME_DP_DAN_RANK_1_DAN = 7 - GAME_DP_DAN_RANK_2_DAN = 8 - GAME_DP_DAN_RANK_3_DAN = 9 - GAME_DP_DAN_RANK_4_DAN = 10 - GAME_DP_DAN_RANK_5_DAN = 11 - GAME_DP_DAN_RANK_6_DAN = 12 - GAME_DP_DAN_RANK_7_DAN = 13 - GAME_DP_DAN_RANK_8_DAN = 14 - GAME_DP_DAN_RANK_9_DAN = 15 - GAME_DP_DAN_RANK_10_DAN = 16 - GAME_DP_DAN_RANK_CHUDEN = 17 - GAME_DP_DAN_RANK_KAIDEN = 18 + GAME_DP_DAN_RANK_7_KYU: Final[int] = 0 + GAME_DP_DAN_RANK_6_KYU: Final[int] = 1 + GAME_DP_DAN_RANK_5_KYU: Final[int] = 2 + GAME_DP_DAN_RANK_4_KYU: Final[int] = 3 + GAME_DP_DAN_RANK_3_KYU: Final[int] = 4 + GAME_DP_DAN_RANK_2_KYU: Final[int] = 5 + GAME_DP_DAN_RANK_1_KYU: Final[int] = 6 + GAME_DP_DAN_RANK_1_DAN: Final[int] = 7 + GAME_DP_DAN_RANK_2_DAN: Final[int] = 8 + GAME_DP_DAN_RANK_3_DAN: Final[int] = 9 + GAME_DP_DAN_RANK_4_DAN: Final[int] = 10 + GAME_DP_DAN_RANK_5_DAN: Final[int] = 11 + GAME_DP_DAN_RANK_6_DAN: Final[int] = 12 + GAME_DP_DAN_RANK_7_DAN: Final[int] = 13 + GAME_DP_DAN_RANK_8_DAN: Final[int] = 14 + GAME_DP_DAN_RANK_9_DAN: Final[int] = 15 + GAME_DP_DAN_RANK_10_DAN: Final[int] = 16 + GAME_DP_DAN_RANK_CHUDEN: Final[int] = 17 + GAME_DP_DAN_RANK_KAIDEN: Final[int] = 18 - FAVORITE_LIST_LENGTH = 20 + FAVORITE_LIST_LENGTH: Final[int] = 20 def previous_version(self) -> Optional[IIDXBase]: return IIDXCopula(self.data, self.config, self.model) @@ -1883,7 +1883,7 @@ class IIDXSinobuz(IIDXCourse, IIDXBase): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() play_stats = self.get_play_statistics(userid) # Track play counts diff --git a/bemani/backend/iidx/spada.py b/bemani/backend/iidx/spada.py index 0df4ba0..1f9e67c 100644 --- a/bemani/backend/iidx/spada.py +++ b/bemani/backend/iidx/spada.py @@ -1,8 +1,8 @@ # vim: set fileencoding=utf-8 -import copy import random import struct from typing import Optional, Dict, List, Tuple, Any +from typing_extensions import Final from bemani.backend.iidx.base import IIDXBase from bemani.backend.iidx.tricoro import IIDXTricoro @@ -14,73 +14,73 @@ from bemani.protocol import Node class IIDXSpada(IIDXBase): - name = 'Beatmania IIDX SPADA' - version = VersionConstants.IIDX_SPADA + name: str = 'Beatmania IIDX SPADA' + version: int = VersionConstants.IIDX_SPADA - GAME_CLTYPE_SINGLE = 0 - GAME_CLTYPE_DOUBLE = 1 + GAME_CLTYPE_SINGLE: Final[int] = 0 + GAME_CLTYPE_DOUBLE: Final[int] = 1 - DAN_STAGES_SINGLE = 4 - DAN_STAGES_DOUBLE = 3 + DAN_STAGES_SINGLE: Final[int] = 4 + DAN_STAGES_DOUBLE: Final[int] = 3 - GAME_CLEAR_STATUS_NO_PLAY = 0 - GAME_CLEAR_STATUS_FAILED = 1 - GAME_CLEAR_STATUS_ASSIST_CLEAR = 2 - GAME_CLEAR_STATUS_EASY_CLEAR = 3 - GAME_CLEAR_STATUS_CLEAR = 4 - GAME_CLEAR_STATUS_HARD_CLEAR = 5 - GAME_CLEAR_STATUS_EX_HARD_CLEAR = 6 - GAME_CLEAR_STATUS_FULL_COMBO = 7 + GAME_CLEAR_STATUS_NO_PLAY: Final[int] = 0 + GAME_CLEAR_STATUS_FAILED: Final[int] = 1 + GAME_CLEAR_STATUS_ASSIST_CLEAR: Final[int] = 2 + GAME_CLEAR_STATUS_EASY_CLEAR: Final[int] = 3 + GAME_CLEAR_STATUS_CLEAR: Final[int] = 4 + GAME_CLEAR_STATUS_HARD_CLEAR: Final[int] = 5 + GAME_CLEAR_STATUS_EX_HARD_CLEAR: Final[int] = 6 + GAME_CLEAR_STATUS_FULL_COMBO: Final[int] = 7 - GAME_GHOST_TYPE_RIVAL = 1 - GAME_GHOST_TYPE_GLOBAL_TOP = 2 - GAME_GHOST_TYPE_GLOBAL_AVERAGE = 3 - GAME_GHOST_TYPE_LOCAL_TOP = 4 - GAME_GHOST_TYPE_LOCAL_AVERAGE = 5 - GAME_GHOST_TYPE_DAN_TOP = 6 - GAME_GHOST_TYPE_DAN_AVERAGE = 7 - GAME_GHOST_TYPE_RIVAL_TOP = 8 - GAME_GHOST_TYPE_RIVAL_AVERAGE = 9 + GAME_GHOST_TYPE_RIVAL: Final[int] = 1 + GAME_GHOST_TYPE_GLOBAL_TOP: Final[int] = 2 + GAME_GHOST_TYPE_GLOBAL_AVERAGE: Final[int] = 3 + GAME_GHOST_TYPE_LOCAL_TOP: Final[int] = 4 + GAME_GHOST_TYPE_LOCAL_AVERAGE: Final[int] = 5 + GAME_GHOST_TYPE_DAN_TOP: Final[int] = 6 + GAME_GHOST_TYPE_DAN_AVERAGE: Final[int] = 7 + GAME_GHOST_TYPE_RIVAL_TOP: Final[int] = 8 + GAME_GHOST_TYPE_RIVAL_AVERAGE: Final[int] = 9 - GAME_GHOST_LENGTH = 64 + GAME_GHOST_LENGTH: Final[int] = 64 - GAME_SP_DAN_RANK_7_KYU = 0 - GAME_SP_DAN_RANK_6_KYU = 1 - GAME_SP_DAN_RANK_5_KYU = 2 - GAME_SP_DAN_RANK_4_KYU = 3 - GAME_SP_DAN_RANK_3_KYU = 4 - GAME_SP_DAN_RANK_2_KYU = 5 - GAME_SP_DAN_RANK_1_KYU = 6 - GAME_SP_DAN_RANK_1_DAN = 7 - GAME_SP_DAN_RANK_2_DAN = 8 - GAME_SP_DAN_RANK_3_DAN = 9 - GAME_SP_DAN_RANK_4_DAN = 10 - GAME_SP_DAN_RANK_5_DAN = 11 - GAME_SP_DAN_RANK_6_DAN = 12 - GAME_SP_DAN_RANK_7_DAN = 13 - GAME_SP_DAN_RANK_8_DAN = 14 - GAME_SP_DAN_RANK_9_DAN = 15 - GAME_SP_DAN_RANK_10_DAN = 16 - GAME_SP_DAN_RANK_KAIDEN = 17 + GAME_SP_DAN_RANK_7_KYU: Final[int] = 0 + GAME_SP_DAN_RANK_6_KYU: Final[int] = 1 + GAME_SP_DAN_RANK_5_KYU: Final[int] = 2 + GAME_SP_DAN_RANK_4_KYU: Final[int] = 3 + GAME_SP_DAN_RANK_3_KYU: Final[int] = 4 + GAME_SP_DAN_RANK_2_KYU: Final[int] = 5 + GAME_SP_DAN_RANK_1_KYU: Final[int] = 6 + GAME_SP_DAN_RANK_1_DAN: Final[int] = 7 + GAME_SP_DAN_RANK_2_DAN: Final[int] = 8 + GAME_SP_DAN_RANK_3_DAN: Final[int] = 9 + GAME_SP_DAN_RANK_4_DAN: Final[int] = 10 + GAME_SP_DAN_RANK_5_DAN: Final[int] = 11 + GAME_SP_DAN_RANK_6_DAN: Final[int] = 12 + GAME_SP_DAN_RANK_7_DAN: Final[int] = 13 + GAME_SP_DAN_RANK_8_DAN: Final[int] = 14 + GAME_SP_DAN_RANK_9_DAN: Final[int] = 15 + GAME_SP_DAN_RANK_10_DAN: Final[int] = 16 + GAME_SP_DAN_RANK_KAIDEN: Final[int] = 17 - GAME_DP_DAN_RANK_5_KYU = 0 - GAME_DP_DAN_RANK_4_KYU = 1 - GAME_DP_DAN_RANK_3_KYU = 2 - GAME_DP_DAN_RANK_2_KYU = 3 - GAME_DP_DAN_RANK_1_KYU = 4 - GAME_DP_DAN_RANK_1_DAN = 5 - GAME_DP_DAN_RANK_2_DAN = 6 - GAME_DP_DAN_RANK_3_DAN = 7 - GAME_DP_DAN_RANK_4_DAN = 8 - GAME_DP_DAN_RANK_5_DAN = 9 - GAME_DP_DAN_RANK_6_DAN = 10 - GAME_DP_DAN_RANK_7_DAN = 11 - GAME_DP_DAN_RANK_8_DAN = 12 - GAME_DP_DAN_RANK_9_DAN = 13 - GAME_DP_DAN_RANK_10_DAN = 14 - GAME_DP_DAN_RANK_KAIDEN = 15 + GAME_DP_DAN_RANK_5_KYU: Final[int] = 0 + GAME_DP_DAN_RANK_4_KYU: Final[int] = 1 + GAME_DP_DAN_RANK_3_KYU: Final[int] = 2 + GAME_DP_DAN_RANK_2_KYU: Final[int] = 3 + GAME_DP_DAN_RANK_1_KYU: Final[int] = 4 + GAME_DP_DAN_RANK_1_DAN: Final[int] = 5 + GAME_DP_DAN_RANK_2_DAN: Final[int] = 6 + GAME_DP_DAN_RANK_3_DAN: Final[int] = 7 + GAME_DP_DAN_RANK_4_DAN: Final[int] = 8 + GAME_DP_DAN_RANK_5_DAN: Final[int] = 9 + GAME_DP_DAN_RANK_6_DAN: Final[int] = 10 + GAME_DP_DAN_RANK_7_DAN: Final[int] = 11 + GAME_DP_DAN_RANK_8_DAN: Final[int] = 12 + GAME_DP_DAN_RANK_9_DAN: Final[int] = 13 + GAME_DP_DAN_RANK_10_DAN: Final[int] = 14 + GAME_DP_DAN_RANK_KAIDEN: Final[int] = 15 - FAVORITE_LIST_LENGTH = 20 + FAVORITE_LIST_LENGTH: Final[int] = 20 def previous_version(self) -> Optional[IIDXBase]: return IIDXTricoro(self.data, self.config, self.model) @@ -1467,7 +1467,7 @@ class IIDXSpada(IIDXBase): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() play_stats = self.get_play_statistics(userid) # Track play counts diff --git a/bemani/backend/iidx/stubs.py b/bemani/backend/iidx/stubs.py index bbd99ba..77e7b32 100644 --- a/bemani/backend/iidx/stubs.py +++ b/bemani/backend/iidx/stubs.py @@ -7,14 +7,14 @@ from bemani.common import VersionConstants class IIDX1stStyle(IIDXBase): - name = 'Beatmania IIDX 1st style & substream' - version = VersionConstants.IIDX + name: str = 'Beatmania IIDX 1st style & substream' + version: int = VersionConstants.IIDX class IIDX2ndStyle(IIDXBase): - name = 'Beatmania IIDX 2nd style' - version = VersionConstants.IIDX_2ND_STYLE + name: str = 'Beatmania IIDX 2nd style' + version: int = VersionConstants.IIDX_2ND_STYLE def previous_version(self) -> Optional[IIDXBase]: return IIDX1stStyle(self.data, self.config, self.model) @@ -22,8 +22,8 @@ class IIDX2ndStyle(IIDXBase): class IIDX3rdStyle(IIDXBase): - name = 'Beatmania IIDX 3rd style' - version = VersionConstants.IIDX_3RD_STYLE + name: str = 'Beatmania IIDX 3rd style' + version: int = VersionConstants.IIDX_3RD_STYLE def previous_version(self) -> Optional[IIDXBase]: return IIDX2ndStyle(self.data, self.config, self.model) @@ -31,8 +31,8 @@ class IIDX3rdStyle(IIDXBase): class IIDX4thStyle(IIDXBase): - name = 'Beatmania IIDX 4th style' - version = VersionConstants.IIDX_4TH_STYLE + name: str = 'Beatmania IIDX 4th style' + version: int = VersionConstants.IIDX_4TH_STYLE def previous_version(self) -> Optional[IIDXBase]: return IIDX3rdStyle(self.data, self.config, self.model) @@ -40,8 +40,8 @@ class IIDX4thStyle(IIDXBase): class IIDX5thStyle(IIDXBase): - name = 'Beatmania IIDX 5th style' - version = VersionConstants.IIDX_5TH_STYLE + name: str = 'Beatmania IIDX 5th style' + version: int = VersionConstants.IIDX_5TH_STYLE def previous_version(self) -> Optional[IIDXBase]: return IIDX4thStyle(self.data, self.config, self.model) @@ -49,8 +49,8 @@ class IIDX5thStyle(IIDXBase): class IIDX6thStyle(IIDXBase): - name = 'Beatmania IIDX 6th style' - version = VersionConstants.IIDX_6TH_STYLE + name: str = 'Beatmania IIDX 6th style' + version: int = VersionConstants.IIDX_6TH_STYLE def previous_version(self) -> Optional[IIDXBase]: return IIDX5thStyle(self.data, self.config, self.model) @@ -58,8 +58,8 @@ class IIDX6thStyle(IIDXBase): class IIDX7thStyle(IIDXBase): - name = 'Beatmania IIDX 7th style' - version = VersionConstants.IIDX_7TH_STYLE + name: str = 'Beatmania IIDX 7th style' + version: int = VersionConstants.IIDX_7TH_STYLE def previous_version(self) -> Optional[IIDXBase]: return IIDX6thStyle(self.data, self.config, self.model) @@ -67,8 +67,8 @@ class IIDX7thStyle(IIDXBase): class IIDX8thStyle(IIDXBase): - name = 'Beatmania IIDX 8th style' - version = VersionConstants.IIDX_8TH_STYLE + name: str = 'Beatmania IIDX 8th style' + version: int = VersionConstants.IIDX_8TH_STYLE def previous_version(self) -> Optional[IIDXBase]: return IIDX7thStyle(self.data, self.config, self.model) @@ -76,8 +76,8 @@ class IIDX8thStyle(IIDXBase): class IIDX9thStyle(IIDXBase): - name = 'Beatmania IIDX 9th style' - version = VersionConstants.IIDX_9TH_STYLE + name: str = 'Beatmania IIDX 9th style' + version: int = VersionConstants.IIDX_9TH_STYLE def previous_version(self) -> Optional[IIDXBase]: return IIDX8thStyle(self.data, self.config, self.model) @@ -85,8 +85,8 @@ class IIDX9thStyle(IIDXBase): class IIDX10thStyle(IIDXBase): - name = 'Beatmania IIDX 10th style' - version = VersionConstants.IIDX_10TH_STYLE + name: str = 'Beatmania IIDX 10th style' + version: int = VersionConstants.IIDX_10TH_STYLE def previous_version(self) -> Optional[IIDXBase]: return IIDX9thStyle(self.data, self.config, self.model) @@ -94,8 +94,8 @@ class IIDX10thStyle(IIDXBase): class IIDXRed(IIDXBase): - name = 'Beatmania IIDX RED' - version = VersionConstants.IIDX_RED + name: str = 'Beatmania IIDX RED' + version: int = VersionConstants.IIDX_RED def previous_version(self) -> Optional[IIDXBase]: return IIDX10thStyle(self.data, self.config, self.model) @@ -103,8 +103,8 @@ class IIDXRed(IIDXBase): class IIDXHappySky(IIDXBase): - name = 'Beatmania IIDX HAPPY SKY' - version = VersionConstants.IIDX_HAPPY_SKY + name: str = 'Beatmania IIDX HAPPY SKY' + version: int = VersionConstants.IIDX_HAPPY_SKY def previous_version(self) -> Optional[IIDXBase]: return IIDXRed(self.data, self.config, self.model) @@ -112,8 +112,8 @@ class IIDXHappySky(IIDXBase): class IIDXDistorted(IIDXBase): - name = 'Beatmania IIDX DistorteD' - version = VersionConstants.IIDX_DISTORTED + name: str = 'Beatmania IIDX DistorteD' + version: int = VersionConstants.IIDX_DISTORTED def previous_version(self) -> Optional[IIDXBase]: return IIDXHappySky(self.data, self.config, self.model) @@ -121,8 +121,8 @@ class IIDXDistorted(IIDXBase): class IIDXGold(IIDXBase): - name = 'Beatmania IIDX GOLD' - version = VersionConstants.IIDX_GOLD + name: str = 'Beatmania IIDX GOLD' + version: int = VersionConstants.IIDX_GOLD def previous_version(self) -> Optional[IIDXBase]: return IIDXDistorted(self.data, self.config, self.model) @@ -130,8 +130,8 @@ class IIDXGold(IIDXBase): class IIDXDJTroopers(IIDXBase): - name = 'Beatmania IIDX DJ TROOPERS' - version = VersionConstants.IIDX_DJ_TROOPERS + name: str = 'Beatmania IIDX DJ TROOPERS' + version: int = VersionConstants.IIDX_DJ_TROOPERS def previous_version(self) -> Optional[IIDXBase]: return IIDXGold(self.data, self.config, self.model) @@ -139,8 +139,8 @@ class IIDXDJTroopers(IIDXBase): class IIDXEmpress(IIDXBase): - name = 'Beatmania IIDX EMPRESS' - version = VersionConstants.IIDX_EMPRESS + name: str = 'Beatmania IIDX EMPRESS' + version: int = VersionConstants.IIDX_EMPRESS def previous_version(self) -> Optional[IIDXBase]: return IIDXDJTroopers(self.data, self.config, self.model) @@ -148,8 +148,8 @@ class IIDXEmpress(IIDXBase): class IIDXSirius(IIDXBase): - name = 'Beatmania IIDX SIRIUS' - version = VersionConstants.IIDX_SIRIUS + name: str = 'Beatmania IIDX SIRIUS' + version: int = VersionConstants.IIDX_SIRIUS def previous_version(self) -> Optional[IIDXBase]: return IIDXEmpress(self.data, self.config, self.model) @@ -157,8 +157,8 @@ class IIDXSirius(IIDXBase): class IIDXResortAnthem(IIDXBase): - name = 'Beatmania IIDX Resort Anthem' - version = VersionConstants.IIDX_RESORT_ANTHEM + name: str = 'Beatmania IIDX Resort Anthem' + version: int = VersionConstants.IIDX_RESORT_ANTHEM def previous_version(self) -> Optional[IIDXBase]: return IIDXSirius(self.data, self.config, self.model) @@ -166,8 +166,8 @@ class IIDXResortAnthem(IIDXBase): class IIDXLincle(IIDXBase): - name = 'Beatmania IIDX Lincle' - version = VersionConstants.IIDX_LINCLE + name: str = 'Beatmania IIDX Lincle' + version: int = VersionConstants.IIDX_LINCLE def previous_version(self) -> Optional[IIDXBase]: return IIDXResortAnthem(self.data, self.config, self.model) diff --git a/bemani/backend/iidx/tricoro.py b/bemani/backend/iidx/tricoro.py index 3f9f9a8..b616964 100644 --- a/bemani/backend/iidx/tricoro.py +++ b/bemani/backend/iidx/tricoro.py @@ -1,7 +1,7 @@ # vim: set fileencoding=utf-8 -import copy import random from typing import Optional, Dict, List, Tuple, Any +from typing_extensions import Final from bemani.backend.iidx.base import IIDXBase from bemani.backend.iidx.stubs import IIDXLincle @@ -13,73 +13,73 @@ from bemani.protocol import Node class IIDXTricoro(IIDXBase): - name = 'Beatmania IIDX Tricoro' - version = VersionConstants.IIDX_TRICORO + name: str = 'Beatmania IIDX Tricoro' + version: int = VersionConstants.IIDX_TRICORO - GAME_CLTYPE_SINGLE = 0 - GAME_CLTYPE_DOUBLE = 1 + GAME_CLTYPE_SINGLE: Final[int] = 0 + GAME_CLTYPE_DOUBLE: Final[int] = 1 - DAN_STAGES_SINGLE = 4 - DAN_STAGES_DOUBLE = 3 + DAN_STAGES_SINGLE: Final[int] = 4 + DAN_STAGES_DOUBLE: Final[int] = 3 - GAME_CLEAR_STATUS_NO_PLAY = 0 - GAME_CLEAR_STATUS_FAILED = 1 - GAME_CLEAR_STATUS_ASSIST_CLEAR = 2 - GAME_CLEAR_STATUS_EASY_CLEAR = 3 - GAME_CLEAR_STATUS_CLEAR = 4 - GAME_CLEAR_STATUS_HARD_CLEAR = 5 - GAME_CLEAR_STATUS_EX_HARD_CLEAR = 6 - GAME_CLEAR_STATUS_FULL_COMBO = 7 + GAME_CLEAR_STATUS_NO_PLAY: Final[int] = 0 + GAME_CLEAR_STATUS_FAILED: Final[int] = 1 + GAME_CLEAR_STATUS_ASSIST_CLEAR: Final[int] = 2 + GAME_CLEAR_STATUS_EASY_CLEAR: Final[int] = 3 + GAME_CLEAR_STATUS_CLEAR: Final[int] = 4 + GAME_CLEAR_STATUS_HARD_CLEAR: Final[int] = 5 + GAME_CLEAR_STATUS_EX_HARD_CLEAR: Final[int] = 6 + GAME_CLEAR_STATUS_FULL_COMBO: Final[int] = 7 - GAME_GHOST_TYPE_RIVAL = 1 - GAME_GHOST_TYPE_GLOBAL_TOP = 2 - GAME_GHOST_TYPE_GLOBAL_AVERAGE = 3 - GAME_GHOST_TYPE_LOCAL_TOP = 4 - GAME_GHOST_TYPE_LOCAL_AVERAGE = 5 - GAME_GHOST_TYPE_DAN_TOP = 6 - GAME_GHOST_TYPE_DAN_AVERAGE = 7 - GAME_GHOST_TYPE_RIVAL_TOP = 8 - GAME_GHOST_TYPE_RIVAL_AVERAGE = 9 + GAME_GHOST_TYPE_RIVAL: Final[int] = 1 + GAME_GHOST_TYPE_GLOBAL_TOP: Final[int] = 2 + GAME_GHOST_TYPE_GLOBAL_AVERAGE: Final[int] = 3 + GAME_GHOST_TYPE_LOCAL_TOP: Final[int] = 4 + GAME_GHOST_TYPE_LOCAL_AVERAGE: Final[int] = 5 + GAME_GHOST_TYPE_DAN_TOP: Final[int] = 6 + GAME_GHOST_TYPE_DAN_AVERAGE: Final[int] = 7 + GAME_GHOST_TYPE_RIVAL_TOP: Final[int] = 8 + GAME_GHOST_TYPE_RIVAL_AVERAGE: Final[int] = 9 - GAME_GHOST_LENGTH = 64 + GAME_GHOST_LENGTH: Final[int] = 64 - GAME_SP_DAN_RANK_7_KYU = 0 - GAME_SP_DAN_RANK_6_KYU = 1 - GAME_SP_DAN_RANK_5_KYU = 2 - GAME_SP_DAN_RANK_4_KYU = 3 - GAME_SP_DAN_RANK_3_KYU = 4 - GAME_SP_DAN_RANK_2_KYU = 5 - GAME_SP_DAN_RANK_1_KYU = 6 - GAME_SP_DAN_RANK_1_DAN = 7 - GAME_SP_DAN_RANK_2_DAN = 8 - GAME_SP_DAN_RANK_3_DAN = 9 - GAME_SP_DAN_RANK_4_DAN = 10 - GAME_SP_DAN_RANK_5_DAN = 11 - GAME_SP_DAN_RANK_6_DAN = 12 - GAME_SP_DAN_RANK_7_DAN = 13 - GAME_SP_DAN_RANK_8_DAN = 14 - GAME_SP_DAN_RANK_9_DAN = 15 - GAME_SP_DAN_RANK_10_DAN = 16 - GAME_SP_DAN_RANK_KAIDEN = 17 + GAME_SP_DAN_RANK_7_KYU: Final[int] = 0 + GAME_SP_DAN_RANK_6_KYU: Final[int] = 1 + GAME_SP_DAN_RANK_5_KYU: Final[int] = 2 + GAME_SP_DAN_RANK_4_KYU: Final[int] = 3 + GAME_SP_DAN_RANK_3_KYU: Final[int] = 4 + GAME_SP_DAN_RANK_2_KYU: Final[int] = 5 + GAME_SP_DAN_RANK_1_KYU: Final[int] = 6 + GAME_SP_DAN_RANK_1_DAN: Final[int] = 7 + GAME_SP_DAN_RANK_2_DAN: Final[int] = 8 + GAME_SP_DAN_RANK_3_DAN: Final[int] = 9 + GAME_SP_DAN_RANK_4_DAN: Final[int] = 10 + GAME_SP_DAN_RANK_5_DAN: Final[int] = 11 + GAME_SP_DAN_RANK_6_DAN: Final[int] = 12 + GAME_SP_DAN_RANK_7_DAN: Final[int] = 13 + GAME_SP_DAN_RANK_8_DAN: Final[int] = 14 + GAME_SP_DAN_RANK_9_DAN: Final[int] = 15 + GAME_SP_DAN_RANK_10_DAN: Final[int] = 16 + GAME_SP_DAN_RANK_KAIDEN: Final[int] = 17 - GAME_DP_DAN_RANK_5_KYU = 0 - GAME_DP_DAN_RANK_4_KYU = 1 - GAME_DP_DAN_RANK_3_KYU = 2 - GAME_DP_DAN_RANK_2_KYU = 3 - GAME_DP_DAN_RANK_1_KYU = 4 - GAME_DP_DAN_RANK_1_DAN = 5 - GAME_DP_DAN_RANK_2_DAN = 6 - GAME_DP_DAN_RANK_3_DAN = 7 - GAME_DP_DAN_RANK_4_DAN = 8 - GAME_DP_DAN_RANK_5_DAN = 9 - GAME_DP_DAN_RANK_6_DAN = 10 - GAME_DP_DAN_RANK_7_DAN = 11 - GAME_DP_DAN_RANK_8_DAN = 12 - GAME_DP_DAN_RANK_9_DAN = 13 - GAME_DP_DAN_RANK_10_DAN = 14 - GAME_DP_DAN_RANK_KAIDEN = 15 + GAME_DP_DAN_RANK_5_KYU: Final[int] = 0 + GAME_DP_DAN_RANK_4_KYU: Final[int] = 1 + GAME_DP_DAN_RANK_3_KYU: Final[int] = 2 + GAME_DP_DAN_RANK_2_KYU: Final[int] = 3 + GAME_DP_DAN_RANK_1_KYU: Final[int] = 4 + GAME_DP_DAN_RANK_1_DAN: Final[int] = 5 + GAME_DP_DAN_RANK_2_DAN: Final[int] = 6 + GAME_DP_DAN_RANK_3_DAN: Final[int] = 7 + GAME_DP_DAN_RANK_4_DAN: Final[int] = 8 + GAME_DP_DAN_RANK_5_DAN: Final[int] = 9 + GAME_DP_DAN_RANK_6_DAN: Final[int] = 10 + GAME_DP_DAN_RANK_7_DAN: Final[int] = 11 + GAME_DP_DAN_RANK_8_DAN: Final[int] = 12 + GAME_DP_DAN_RANK_9_DAN: Final[int] = 13 + GAME_DP_DAN_RANK_10_DAN: Final[int] = 14 + GAME_DP_DAN_RANK_KAIDEN: Final[int] = 15 - FAVORITE_LIST_LENGTH = 20 + FAVORITE_LIST_LENGTH: Final[int] = 20 def previous_version(self) -> Optional[IIDXBase]: return IIDXLincle(self.data, self.config, self.model) @@ -1259,7 +1259,7 @@ class IIDXTricoro(IIDXBase): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() play_stats = self.get_play_statistics(userid) # Track play counts, DJ points and options diff --git a/bemani/backend/jubeat/base.py b/bemani/backend/jubeat/base.py index 49d96f3..180c146 100644 --- a/bemani/backend/jubeat/base.py +++ b/bemani/backend/jubeat/base.py @@ -1,5 +1,6 @@ # vim: set fileencoding=utf-8 from typing import Dict, List, Optional +from typing_extensions import Final from bemani.backend.base import Base from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler @@ -14,27 +15,27 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): profiles based on refid, creating new profiles, looking up and saving scores. """ - game = GameConstants.JUBEAT + game: GameConstants = GameConstants.JUBEAT - GAME_FLAG_BIT_PLAYED = 0x1 - GAME_FLAG_BIT_CLEARED = 0x2 - GAME_FLAG_BIT_FULL_COMBO = 0x4 - GAME_FLAG_BIT_EXCELLENT = 0x8 - GAME_FLAG_BIT_NEARLY_FULL_COMBO = 0x10 - GAME_FLAG_BIT_NEARLY_EXCELLENT = 0x20 - GAME_FLAG_BIT_NO_GRAY = 0x40 - GAME_FLAG_BIT_NO_YELLOW = 0x80 + GAME_FLAG_BIT_PLAYED: Final[int] = 0x1 + GAME_FLAG_BIT_CLEARED: Final[int] = 0x2 + GAME_FLAG_BIT_FULL_COMBO: Final[int] = 0x4 + GAME_FLAG_BIT_EXCELLENT: Final[int] = 0x8 + GAME_FLAG_BIT_NEARLY_FULL_COMBO: Final[int] = 0x10 + GAME_FLAG_BIT_NEARLY_EXCELLENT: Final[int] = 0x20 + GAME_FLAG_BIT_NO_GRAY: Final[int] = 0x40 + GAME_FLAG_BIT_NO_YELLOW: Final[int] = 0x80 - PLAY_MEDAL_FAILED = DBConstants.JUBEAT_PLAY_MEDAL_FAILED - PLAY_MEDAL_CLEARED = DBConstants.JUBEAT_PLAY_MEDAL_CLEARED - PLAY_MEDAL_NEARLY_FULL_COMBO = DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO - PLAY_MEDAL_FULL_COMBO = DBConstants.JUBEAT_PLAY_MEDAL_FULL_COMBO - PLAY_MEDAL_NEARLY_EXCELLENT = DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT - PLAY_MEDAL_EXCELLENT = DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT + PLAY_MEDAL_FAILED: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_FAILED + PLAY_MEDAL_CLEARED: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_CLEARED + PLAY_MEDAL_NEARLY_FULL_COMBO: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO + PLAY_MEDAL_FULL_COMBO: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_FULL_COMBO + PLAY_MEDAL_NEARLY_EXCELLENT: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT + PLAY_MEDAL_EXCELLENT: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT - CHART_TYPE_BASIC = 0 - CHART_TYPE_ADVANCED = 1 - CHART_TYPE_EXTREME = 2 + CHART_TYPE_BASIC: Final[int] = 0 + CHART_TYPE_ADVANCED: Final[int] = 1 + CHART_TYPE_EXTREME: Final[int] = 2 def previous_version(self) -> Optional['JubeatBase']: """ diff --git a/bemani/backend/jubeat/clan.py b/bemani/backend/jubeat/clan.py index 39a287b..1829990 100644 --- a/bemani/backend/jubeat/clan.py +++ b/bemani/backend/jubeat/clan.py @@ -1,7 +1,7 @@ # vim: set fileencoding=utf-8 -import copy import random from typing import Any, Dict, List, Optional, Set, Tuple +from typing_extensions import Final from bemani.backend.jubeat.base import JubeatBase from bemani.backend.jubeat.common import ( @@ -30,16 +30,16 @@ class JubeatClan( JubeatBase, ): - name = 'Jubeat Clan' - version = VersionConstants.JUBEAT_CLAN + name: str = 'Jubeat Clan' + version: int = VersionConstants.JUBEAT_CLAN - JBOX_EMBLEM_NORMAL = 1 - JBOX_EMBLEM_PREMIUM = 2 + JBOX_EMBLEM_NORMAL: Final[int] = 1 + JBOX_EMBLEM_PREMIUM: Final[int] = 2 - EVENT_STATUS_OPEN = 0x1 - EVENT_STATUS_COMPLETE = 0x2 + EVENT_STATUS_OPEN: Final[int] = 0x1 + EVENT_STATUS_COMPLETE: Final[int] = 0x2 - EVENTS = { + EVENTS: Dict[int, Dict[str, bool]] = { 5: { 'enabled': False, }, @@ -60,25 +60,25 @@ class JubeatClan( }, } - FIVE_PLAYS_UNLOCK_EVENT_SONG_IDS = set(range(80000301, 80000348)) + FIVE_PLAYS_UNLOCK_EVENT_SONG_IDS: Set[int] = set(range(80000301, 80000348)) - COURSE_STATUS_SEEN = 0x01 - COURSE_STATUS_PLAYED = 0x02 - COURSE_STATUS_CLEARED = 0x04 + COURSE_STATUS_SEEN: Final[int] = 0x01 + COURSE_STATUS_PLAYED: Final[int] = 0x02 + COURSE_STATUS_CLEARED: Final[int] = 0x04 - COURSE_TYPE_PERMANENT = 1 - COURSE_TYPE_TIME_BASED = 2 + COURSE_TYPE_PERMANENT: Final[int] = 1 + COURSE_TYPE_TIME_BASED: Final[int] = 2 - COURSE_CLEAR_SCORE = 1 - COURSE_CLEAR_COMBINED_SCORE = 2 - COURSE_CLEAR_HAZARD = 3 + COURSE_CLEAR_SCORE: Final[int] = 1 + COURSE_CLEAR_COMBINED_SCORE: Final[int] = 2 + COURSE_CLEAR_HAZARD: Final[int] = 3 - COURSE_HAZARD_EXC1 = 1 - COURSE_HAZARD_EXC2 = 2 - COURSE_HAZARD_EXC3 = 3 - COURSE_HAZARD_FC1 = 4 - COURSE_HAZARD_FC2 = 5 - COURSE_HAZARD_FC3 = 6 + COURSE_HAZARD_EXC1: Final[int] = 1 + COURSE_HAZARD_EXC2: Final[int] = 2 + COURSE_HAZARD_EXC3: Final[int] = 3 + COURSE_HAZARD_FC1: Final[int] = 4 + COURSE_HAZARD_FC2: Final[int] = 5 + COURSE_HAZARD_FC3: Final[int] = 6 def previous_version(self) -> Optional[JubeatBase]: return JubeatQubell(self.data, self.config, self.model) @@ -1449,7 +1449,7 @@ class JubeatClan( for chart in [0, 1, 2]: entry = achievement.data.get_dict(str(chart)) if entry.get_int("value") >= bestentry.get_int("value"): - bestentry = copy.deepcopy(entry) + bestentry = entry.clone() bestentry.replace_int("songid", achievement.id) bestentry.replace_int("chart", chart) jubeat_entries.append(bestentry) @@ -1579,7 +1579,7 @@ class JubeatClan( return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() data = request.child('data') # Grab system information diff --git a/bemani/backend/jubeat/course.py b/bemani/backend/jubeat/course.py index 23111b7..d4e045a 100644 --- a/bemani/backend/jubeat/course.py +++ b/bemani/backend/jubeat/course.py @@ -1,5 +1,6 @@ # vim: set fileencoding=utf-8 from typing import Any, Dict, List +from typing_extensions import Final from bemani.data import UserID from bemani.backend.jubeat.base import JubeatBase @@ -7,14 +8,14 @@ from bemani.backend.jubeat.base import JubeatBase class JubeatCourse(JubeatBase): - COURSE_RATING_FAILED = 100 - COURSE_RATING_BRONZE = 200 - COURSE_RATING_SILVER = 300 - COURSE_RATING_GOLD = 400 + COURSE_RATING_FAILED: Final[int] = 100 + COURSE_RATING_BRONZE: Final[int] = 200 + COURSE_RATING_SILVER: Final[int] = 300 + COURSE_RATING_GOLD: Final[int] = 400 - COURSE_REQUIREMENT_SCORE = 100 - COURSE_REQUIREMENT_FULL_COMBO = 200 - COURSE_REQUIREMENT_PERFECT_PERCENT = 300 + COURSE_REQUIREMENT_SCORE: Final[int] = 100 + COURSE_REQUIREMENT_FULL_COMBO: Final[int] = 200 + COURSE_REQUIREMENT_PERFECT_PERCENT: Final[int] = 300 def get_all_courses(self) -> List[Dict[str, Any]]: # List of base courses for Saucer Fulfill+ from BemaniWiki diff --git a/bemani/backend/jubeat/factory.py b/bemani/backend/jubeat/factory.py index 31030cb..b403e27 100644 --- a/bemani/backend/jubeat/factory.py +++ b/bemani/backend/jubeat/factory.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import List, Optional, Type from bemani.backend.base import Base, Factory from bemani.backend.jubeat.stubs import ( @@ -22,7 +22,7 @@ from bemani.data import Config, Data class JubeatFactory(Factory): - MANAGED_CLASSES = [ + MANAGED_CLASSES: List[Type[Base]] = [ Jubeat, JubeatRipples, JubeatRipplesAppend, diff --git a/bemani/backend/jubeat/festo.py b/bemani/backend/jubeat/festo.py index 8ca85f4..28f94ab 100644 --- a/bemani/backend/jubeat/festo.py +++ b/bemani/backend/jubeat/festo.py @@ -9,8 +9,8 @@ from bemani.common import VersionConstants class JubeatFesto(JubeatBase): - name = 'Jubeat Festo' - version = VersionConstants.JUBEAT_FESTO + name: str = 'Jubeat Festo' + version: int = VersionConstants.JUBEAT_FESTO def previous_version(self) -> Optional[JubeatBase]: return JubeatClan(self.data, self.config, self.model) diff --git a/bemani/backend/jubeat/prop.py b/bemani/backend/jubeat/prop.py index a9cbf8f..bdcf09e 100644 --- a/bemani/backend/jubeat/prop.py +++ b/bemani/backend/jubeat/prop.py @@ -1,8 +1,8 @@ # vim: set fileencoding=utf-8 -import copy import math import random from typing import Optional, Dict, List, Any, Set, Tuple +from typing_extensions import Final from bemani.backend.base import Status from bemani.backend.jubeat.common import ( @@ -32,22 +32,22 @@ class JubeatProp( JubeatBase, ): - name = 'Jubeat Prop' - version = VersionConstants.JUBEAT_PROP + name: str = 'Jubeat Prop' + version: int = VersionConstants.JUBEAT_PROP - GAME_COURSE_REQUIREMENT_SCORE = 1 - GAME_COURSE_REQUIREMENT_FULL_COMBO = 2 - GAME_COURSE_REQUIREMENT_PERFECT_PERCENT = 3 + GAME_COURSE_REQUIREMENT_SCORE: Final[int] = 1 + GAME_COURSE_REQUIREMENT_FULL_COMBO: Final[int] = 2 + GAME_COURSE_REQUIREMENT_PERFECT_PERCENT: Final[int] = 3 - GAME_COURSE_RATING_FAILED = 1 - GAME_COURSE_RATING_BRONZE = 2 - GAME_COURSE_RATING_SILVER = 3 - GAME_COURSE_RATING_GOLD = 4 + GAME_COURSE_RATING_FAILED: Final[int] = 1 + GAME_COURSE_RATING_BRONZE: Final[int] = 2 + GAME_COURSE_RATING_SILVER: Final[int] = 3 + GAME_COURSE_RATING_GOLD: Final[int] = 4 - JBOX_EMBLEM_NORMAL = 1 - JBOX_EMBLEM_PREMIUM = 2 + JBOX_EMBLEM_NORMAL: Final[int] = 1 + JBOX_EMBLEM_PREMIUM: Final[int] = 2 - EVENTS = { + EVENTS: Dict[int, Dict[str, bool]] = { 5: { 'enabled': False, }, @@ -1085,7 +1085,7 @@ class JubeatProp( return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() data = request.child('data') # Grab system information diff --git a/bemani/backend/jubeat/qubell.py b/bemani/backend/jubeat/qubell.py index eab0f4f..fdc37be 100644 --- a/bemani/backend/jubeat/qubell.py +++ b/bemani/backend/jubeat/qubell.py @@ -1,7 +1,7 @@ # vim: set fileencoding=utf-8 -import copy import random from typing import Any, Dict, List, Optional, Set, Tuple +from typing_extensions import Final from bemani.backend.base import Status from bemani.backend.jubeat.base import JubeatBase @@ -30,15 +30,15 @@ class JubeatQubell( JubeatBase, ): - name = 'Jubeat Qubell' - version = VersionConstants.JUBEAT_QUBELL + name: str = 'Jubeat Qubell' + version: int = VersionConstants.JUBEAT_QUBELL - JBOX_EMBLEM_NORMAL = 1 - JBOX_EMBLEM_PREMIUM = 2 + JBOX_EMBLEM_NORMAL: Final[int] = 1 + JBOX_EMBLEM_PREMIUM: Final[int] = 2 - ENABLE_GARNET = False + ENABLE_GARNET: Final[bool] = False - EVENTS = { + EVENTS: Dict[int, Dict[str, bool]] = { 5: { 'enabled': False, }, @@ -921,7 +921,7 @@ class JubeatQubell( return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() data = request.child('data') # Grab system information diff --git a/bemani/backend/jubeat/saucer.py b/bemani/backend/jubeat/saucer.py index 90285a6..818d090 100644 --- a/bemani/backend/jubeat/saucer.py +++ b/bemani/backend/jubeat/saucer.py @@ -1,5 +1,4 @@ # vim: set fileencoding=utf-8 -import copy import random from typing import Any, Dict, List, Optional, Tuple @@ -29,8 +28,8 @@ class JubeatSaucer( JubeatBase, ): - name = 'Jubeat Saucer' - version = VersionConstants.JUBEAT_SAUCER + name: str = 'Jubeat Saucer' + version: int = VersionConstants.JUBEAT_SAUCER def previous_version(self) -> Optional[JubeatBase]: return JubeatCopiousAppend(self.data, self.config, self.model) @@ -519,7 +518,7 @@ class JubeatSaucer( return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() data = request.child('data') # Grab player information diff --git a/bemani/backend/jubeat/saucerfulfill.py b/bemani/backend/jubeat/saucerfulfill.py index bdc58b3..3227b58 100644 --- a/bemani/backend/jubeat/saucerfulfill.py +++ b/bemani/backend/jubeat/saucerfulfill.py @@ -1,7 +1,7 @@ # vim: set fileencoding=utf-8 -import copy import random from typing import Any, Dict, List, Optional, Tuple +from typing_extensions import Final from bemani.backend.base import Status from bemani.backend.jubeat.base import JubeatBase @@ -31,17 +31,17 @@ class JubeatSaucerFulfill( JubeatBase, ): - name = 'Jubeat Saucer Fulfill' - version = VersionConstants.JUBEAT_SAUCER_FULFILL + name: str = 'Jubeat Saucer Fulfill' + version: int = VersionConstants.JUBEAT_SAUCER_FULFILL - GAME_COURSE_REQUIREMENT_SCORE = 1 - GAME_COURSE_REQUIREMENT_FULL_COMBO = 2 - GAME_COURSE_REQUIREMENT_PERFECT_PERCENT = 3 + GAME_COURSE_REQUIREMENT_SCORE: Final[int] = 1 + GAME_COURSE_REQUIREMENT_FULL_COMBO: Final[int] = 2 + GAME_COURSE_REQUIREMENT_PERFECT_PERCENT: Final[int] = 3 - GAME_COURSE_RATING_FAILED = 1 - GAME_COURSE_RATING_BRONZE = 2 - GAME_COURSE_RATING_SILVER = 3 - GAME_COURSE_RATING_GOLD = 4 + GAME_COURSE_RATING_FAILED: Final[int] = 1 + GAME_COURSE_RATING_BRONZE: Final[int] = 2 + GAME_COURSE_RATING_SILVER: Final[int] = 3 + GAME_COURSE_RATING_GOLD: Final[int] = 4 def previous_version(self) -> Optional[JubeatBase]: return JubeatSaucer(self.data, self.config, self.model) @@ -600,7 +600,7 @@ class JubeatSaucerFulfill( return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() data = request.child('data') # Grab player information diff --git a/bemani/backend/jubeat/stubs.py b/bemani/backend/jubeat/stubs.py index e3a67bc..aff5ec6 100644 --- a/bemani/backend/jubeat/stubs.py +++ b/bemani/backend/jubeat/stubs.py @@ -7,14 +7,14 @@ from bemani.common import VersionConstants class Jubeat(JubeatBase): - name = 'Jubeat' - version = VersionConstants.JUBEAT + name: str = 'Jubeat' + version: int = VersionConstants.JUBEAT class JubeatRipples(JubeatBase): - name = 'Jubeat Ripples' - version = VersionConstants.JUBEAT_RIPPLES + name: str = 'Jubeat Ripples' + version: int = VersionConstants.JUBEAT_RIPPLES def previous_version(self) -> Optional[JubeatBase]: return Jubeat(self.data, self.config, self.model) @@ -22,8 +22,8 @@ class JubeatRipples(JubeatBase): class JubeatRipplesAppend(JubeatBase): - name = 'Jubeat Ripples Append' - version = VersionConstants.JUBEAT_RIPPLES_APPEND + name: str = 'Jubeat Ripples Append' + version: int = VersionConstants.JUBEAT_RIPPLES_APPEND def previous_version(self) -> Optional[JubeatBase]: return JubeatRipples(self.data, self.config, self.model) @@ -31,8 +31,8 @@ class JubeatRipplesAppend(JubeatBase): class JubeatKnit(JubeatBase): - name = 'Jubeat Knit' - version = VersionConstants.JUBEAT_KNIT + name: str = 'Jubeat Knit' + version: int = VersionConstants.JUBEAT_KNIT def previous_version(self) -> Optional[JubeatBase]: return JubeatRipplesAppend(self.data, self.config, self.model) @@ -40,8 +40,8 @@ class JubeatKnit(JubeatBase): class JubeatKnitAppend(JubeatBase): - name = 'Jubeat Knit Append' - version = VersionConstants.JUBEAT_KNIT_APPEND + name: str = 'Jubeat Knit Append' + version: int = VersionConstants.JUBEAT_KNIT_APPEND def previous_version(self) -> Optional[JubeatBase]: return JubeatKnit(self.data, self.config, self.model) @@ -49,8 +49,8 @@ class JubeatKnitAppend(JubeatBase): class JubeatCopious(JubeatBase): - name = 'Jubeat Copious' - version = VersionConstants.JUBEAT_COPIOUS + name: str = 'Jubeat Copious' + version: int = VersionConstants.JUBEAT_COPIOUS def previous_version(self) -> Optional[JubeatBase]: return JubeatKnitAppend(self.data, self.config, self.model) @@ -58,8 +58,8 @@ class JubeatCopious(JubeatBase): class JubeatCopiousAppend(JubeatBase): - name = 'Jubeat Copious Append' - version = VersionConstants.JUBEAT_COPIOUS_APPEND + name: str = 'Jubeat Copious Append' + version: int = VersionConstants.JUBEAT_COPIOUS_APPEND def previous_version(self) -> Optional[JubeatBase]: return JubeatCopious(self.data, self.config, self.model) diff --git a/bemani/backend/mga/base.py b/bemani/backend/mga/base.py index fec3716..0be3b52 100644 --- a/bemani/backend/mga/base.py +++ b/bemani/backend/mga/base.py @@ -11,7 +11,7 @@ class MetalGearArcadeBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): Base game class for Metal Gear Arcade. """ - game = GameConstants.MGA + game: GameConstants = GameConstants.MGA def previous_version(self) -> Optional['MetalGearArcadeBase']: """ diff --git a/bemani/backend/mga/factory.py b/bemani/backend/mga/factory.py index ecae9e8..c704bd2 100644 --- a/bemani/backend/mga/factory.py +++ b/bemani/backend/mga/factory.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import List, Optional, Type from bemani.backend.base import Base, Factory from bemani.backend.mga.mga import MetalGearArcade @@ -8,7 +8,7 @@ from bemani.data import Config, Data class MetalGearArcadeFactory(Factory): - MANAGED_CLASSES = [ + MANAGED_CLASSES: List[Type[Base]] = [ MetalGearArcade, ] diff --git a/bemani/backend/mga/mga.py b/bemani/backend/mga/mga.py index c87704e..89def3f 100644 --- a/bemani/backend/mga/mga.py +++ b/bemani/backend/mga/mga.py @@ -1,5 +1,4 @@ # vim: set fileencoding=utf-8 -import copy import base64 from typing import List @@ -15,8 +14,8 @@ class MetalGearArcade( MetalGearArcadeBase, ): - name = "Metal Gear Arcade" - version = VersionConstants.MGA + name: str = "Metal Gear Arcade" + version: int = VersionConstants.MGA def __update_shop_name(self, profiledata: bytes) -> None: # Figure out the profile type @@ -150,7 +149,7 @@ class MetalGearArcade( def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile, is_new: bool) -> Profile: # Profile save request, data values are base64 encoded. # d is a CSV, and bin1 is binary data. - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() strdatas: List[bytes] = [] bindatas: List[bytes] = [] diff --git a/bemani/backend/museca/base.py b/bemani/backend/museca/base.py index 425bbd6..d305175 100644 --- a/bemani/backend/museca/base.py +++ b/bemani/backend/museca/base.py @@ -1,5 +1,6 @@ # vim: set fileencoding=utf-8 from typing import Dict, Optional +from typing_extensions import Final from bemani.backend.base import Base from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler @@ -13,25 +14,25 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): Base game class for all Museca version that we support. """ - game = GameConstants.MUSECA + game: GameConstants = GameConstants.MUSECA - CHART_TYPE_GREEN = 0 - CHART_TYPE_ORANGE = 1 - CHART_TYPE_RED = 2 + CHART_TYPE_GREEN: Final[int] = 0 + CHART_TYPE_ORANGE: Final[int] = 1 + CHART_TYPE_RED: Final[int] = 2 - GRADE_DEATH = DBConstants.MUSECA_GRADE_DEATH - GRADE_POOR = DBConstants.MUSECA_GRADE_POOR - GRADE_MEDIOCRE = DBConstants.MUSECA_GRADE_MEDIOCRE - GRADE_GOOD = DBConstants.MUSECA_GRADE_GOOD - GRADE_GREAT = DBConstants.MUSECA_GRADE_GREAT - GRADE_EXCELLENT = DBConstants.MUSECA_GRADE_EXCELLENT - GRADE_SUPERB = DBConstants.MUSECA_GRADE_SUPERB - GRADE_MASTERPIECE = DBConstants.MUSECA_GRADE_MASTERPIECE - GRADE_PERFECT = DBConstants.MUSECA_GRADE_PERFECT + GRADE_DEATH: Final[int] = DBConstants.MUSECA_GRADE_DEATH + GRADE_POOR: Final[int] = DBConstants.MUSECA_GRADE_POOR + GRADE_MEDIOCRE: Final[int] = DBConstants.MUSECA_GRADE_MEDIOCRE + GRADE_GOOD: Final[int] = DBConstants.MUSECA_GRADE_GOOD + GRADE_GREAT: Final[int] = DBConstants.MUSECA_GRADE_GREAT + GRADE_EXCELLENT: Final[int] = DBConstants.MUSECA_GRADE_EXCELLENT + GRADE_SUPERB: Final[int] = DBConstants.MUSECA_GRADE_SUPERB + GRADE_MASTERPIECE: Final[int] = DBConstants.MUSECA_GRADE_MASTERPIECE + GRADE_PERFECT: Final[int] = DBConstants.MUSECA_GRADE_PERFECT - CLEAR_TYPE_FAILED = DBConstants.MUSECA_CLEAR_TYPE_FAILED - CLEAR_TYPE_CLEARED = DBConstants.MUSECA_CLEAR_TYPE_CLEARED - CLEAR_TYPE_FULL_COMBO = DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO + CLEAR_TYPE_FAILED: Final[int] = DBConstants.MUSECA_CLEAR_TYPE_FAILED + CLEAR_TYPE_CLEARED: Final[int] = DBConstants.MUSECA_CLEAR_TYPE_CLEARED + CLEAR_TYPE_FULL_COMBO: Final[int] = DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO def __init__(self, data: Data, config: Config, model: Model) -> None: super().__init__(data, config, model) diff --git a/bemani/backend/museca/common.py b/bemani/backend/museca/common.py index cc33ca5..ab3d987 100644 --- a/bemani/backend/museca/common.py +++ b/bemani/backend/museca/common.py @@ -1,4 +1,3 @@ - from bemani.backend.museca.base import MusecaBase from bemani.common import ID from bemani.protocol import Node diff --git a/bemani/backend/museca/museca1.py b/bemani/backend/museca/museca1.py index 891d754..d56e14b 100644 --- a/bemani/backend/museca/museca1.py +++ b/bemani/backend/museca/museca1.py @@ -1,5 +1,5 @@ -import copy from typing import Any, Dict +from typing_extensions import Final from bemani.backend.ess import EventLogHandler from bemani.backend.museca.base import MusecaBase @@ -29,29 +29,29 @@ class Museca1( MusecaBase, ): - name = "MÚSECA" - version = VersionConstants.MUSECA + name: str = "MÚSECA" + version: int = VersionConstants.MUSECA - GAME_LIMITED_LOCKED = 1 - GAME_LIMITED_UNLOCKABLE = 2 - GAME_LIMITED_UNLOCKED = 3 + GAME_LIMITED_LOCKED: Final[int] = 1 + GAME_LIMITED_UNLOCKABLE: Final[int] = 2 + GAME_LIMITED_UNLOCKED: Final[int] = 3 - GAME_CATALOG_TYPE_SONG = 0 - GAME_CATALOG_TYPE_GRAFICA = 15 - GAME_CATALOG_TYPE_MISSION = 16 + GAME_CATALOG_TYPE_SONG: Final[int] = 0 + GAME_CATALOG_TYPE_GRAFICA: Final[int] = 15 + GAME_CATALOG_TYPE_MISSION: Final[int] = 16 - GAME_GRADE_DEATH = 0 - GAME_GRADE_POOR = 1 - GAME_GRADE_MEDIOCRE = 2 - GAME_GRADE_GOOD = 3 - GAME_GRADE_GREAT = 4 - GAME_GRADE_EXCELLENT = 5 - GAME_GRADE_SUPERB = 6 - GAME_GRADE_MASTERPIECE = 7 + GAME_GRADE_DEATH: Final[int] = 0 + GAME_GRADE_POOR: Final[int] = 1 + GAME_GRADE_MEDIOCRE: Final[int] = 2 + GAME_GRADE_GOOD: Final[int] = 3 + GAME_GRADE_GREAT: Final[int] = 4 + GAME_GRADE_EXCELLENT: Final[int] = 5 + GAME_GRADE_SUPERB: Final[int] = 6 + GAME_GRADE_MASTERPIECE: Final[int] = 7 - GAME_CLEAR_TYPE_FAILED = 1 - GAME_CLEAR_TYPE_CLEARED = 2 - GAME_CLEAR_TYPE_FULL_COMBO = 4 + GAME_CLEAR_TYPE_FAILED: Final[int] = 1 + GAME_CLEAR_TYPE_CLEARED: Final[int] = 2 + GAME_CLEAR_TYPE_FULL_COMBO: Final[int] = 4 @classmethod def get_settings(cls) -> Dict[str, Any]: @@ -280,7 +280,7 @@ class Museca1( return game def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() # Update blaster energy and in-game currencies earned_gamecoin_packet = request.child_value('earned_gamecoin_packet') diff --git a/bemani/backend/museca/museca1plus.py b/bemani/backend/museca/museca1plus.py index f78ab0f..fafd1fc 100644 --- a/bemani/backend/museca/museca1plus.py +++ b/bemani/backend/museca/museca1plus.py @@ -1,5 +1,5 @@ -import copy from typing import Optional, Dict, Any +from typing_extensions import Final from bemani.backend.ess import EventLogHandler from bemani.backend.museca.base import MusecaBase @@ -30,30 +30,30 @@ class Museca1Plus( MusecaBase, ): - name = "MÚSECA 1+1/2" - version = VersionConstants.MUSECA_1_PLUS + name: str = "MÚSECA 1+1/2" + version: int = VersionConstants.MUSECA_1_PLUS - GAME_LIMITED_LOCKED = 1 - GAME_LIMITED_UNLOCKABLE = 2 - GAME_LIMITED_UNLOCKED = 3 + GAME_LIMITED_LOCKED: Final[int] = 1 + GAME_LIMITED_UNLOCKABLE: Final[int] = 2 + GAME_LIMITED_UNLOCKED: Final[int] = 3 - GAME_CATALOG_TYPE_SONG = 0 - GAME_CATALOG_TYPE_GRAFICA = 15 - GAME_CATALOG_TYPE_MISSION = 16 + GAME_CATALOG_TYPE_SONG: Final[int] = 0 + GAME_CATALOG_TYPE_GRAFICA: Final[int] = 15 + GAME_CATALOG_TYPE_MISSION: Final[int] = 16 - GAME_GRADE_DEATH = 0 - GAME_GRADE_POOR = 1 - GAME_GRADE_MEDIOCRE = 2 - GAME_GRADE_GOOD = 3 - GAME_GRADE_GREAT = 4 - GAME_GRADE_EXCELLENT = 5 - GAME_GRADE_SUPERB = 6 - GAME_GRADE_MASTERPIECE = 7 - GAME_GRADE_PERFECT = 8 + GAME_GRADE_DEATH: Final[int] = 0 + GAME_GRADE_POOR: Final[int] = 1 + GAME_GRADE_MEDIOCRE: Final[int] = 2 + GAME_GRADE_GOOD: Final[int] = 3 + GAME_GRADE_GREAT: Final[int] = 4 + GAME_GRADE_EXCELLENT: Final[int] = 5 + GAME_GRADE_SUPERB: Final[int] = 6 + GAME_GRADE_MASTERPIECE: Final[int] = 7 + GAME_GRADE_PERFECT: Final[int] = 8 - GAME_CLEAR_TYPE_FAILED = 1 - GAME_CLEAR_TYPE_CLEARED = 2 - GAME_CLEAR_TYPE_FULL_COMBO = 4 + GAME_CLEAR_TYPE_FAILED: Final[int] = 1 + GAME_CLEAR_TYPE_CLEARED: Final[int] = 2 + GAME_CLEAR_TYPE_FULL_COMBO: Final[int] = 4 def previous_version(self) -> Optional[MusecaBase]: return Museca1(self.data, self.config, self.model) @@ -418,7 +418,7 @@ class Museca1Plus( return game def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() # Update blaster energy and in-game currencies earned_gamecoin_packet = request.child_value('earned_gamecoin_packet') diff --git a/bemani/backend/popn/base.py b/bemani/backend/popn/base.py index b2bd182..c703c19 100644 --- a/bemani/backend/popn/base.py +++ b/bemani/backend/popn/base.py @@ -1,5 +1,6 @@ # vim: set fileencoding=utf-8 from typing import Dict, Optional, Sequence +from typing_extensions import Final from bemani.backend.base import Base from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler @@ -15,36 +16,36 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): scores. """ - game = GameConstants.POPN_MUSIC + game: GameConstants = GameConstants.POPN_MUSIC # 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_DIAMOND_FAILED = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED - PLAY_MEDAL_STAR_FAILED = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED - PLAY_MEDAL_EASY_CLEAR = DBConstants.POPN_MUSIC_PLAY_MEDAL_EASY_CLEAR - PLAY_MEDAL_CIRCLE_CLEARED = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED - PLAY_MEDAL_DIAMOND_CLEARED = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED - PLAY_MEDAL_STAR_CLEARED = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_CLEARED - PLAY_MEDAL_CIRCLE_FULL_COMBO = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO - PLAY_MEDAL_DIAMOND_FULL_COMBO = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO - PLAY_MEDAL_STAR_FULL_COMBO = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO - PLAY_MEDAL_PERFECT = DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT + PLAY_MEDAL_NO_PLAY: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_NO_PLAY + PLAY_MEDAL_CIRCLE_FAILED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED + PLAY_MEDAL_DIAMOND_FAILED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED + PLAY_MEDAL_STAR_FAILED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED + PLAY_MEDAL_EASY_CLEAR: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_EASY_CLEAR + PLAY_MEDAL_CIRCLE_CLEARED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED + PLAY_MEDAL_DIAMOND_CLEARED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED + PLAY_MEDAL_STAR_CLEARED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_CLEARED + PLAY_MEDAL_CIRCLE_FULL_COMBO: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO + PLAY_MEDAL_DIAMOND_FULL_COMBO: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO + PLAY_MEDAL_STAR_FULL_COMBO: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO + PLAY_MEDAL_PERFECT: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT # Chart type, as saved into/loaded from the DB, and returned to game - CHART_TYPE_EASY = 0 - CHART_TYPE_NORMAL = 1 - CHART_TYPE_HYPER = 2 - CHART_TYPE_EX = 3 + CHART_TYPE_EASY: Final[int] = 0 + CHART_TYPE_NORMAL: Final[int] = 1 + CHART_TYPE_HYPER: Final[int] = 2 + CHART_TYPE_EX: Final[int] = 3 # Old profile lookup type, for loading profile by ID - NEW_PROFILE_ONLY = 0 - OLD_PROFILE_ONLY = 1 - OLD_PROFILE_FALLTHROUGH = 2 + NEW_PROFILE_ONLY: Final[int] = 0 + OLD_PROFILE_ONLY: Final[int] = 1 + OLD_PROFILE_FALLTHROUGH: Final[int] = 2 # Pop'n Music in particular requires non-expired profiles to do conversions # properly. - supports_expired_profiles = False + supports_expired_profiles: bool = False def previous_version(self) -> Optional['PopnMusicBase']: """ diff --git a/bemani/backend/popn/common.py b/bemani/backend/popn/common.py index d251ba3..9c370f5 100644 --- a/bemani/backend/popn/common.py +++ b/bemani/backend/popn/common.py @@ -1,7 +1,6 @@ # vim: set fileencoding=utf-8 -from abc import ABCMeta, abstractmethod +from abc import ABC, abstractmethod import binascii -import copy import random from typing import Any, Dict, List, Optional, Tuple from typing_extensions import Final @@ -12,7 +11,7 @@ from bemani.data import Data, UserID, Achievement, Link from bemani.protocol import Node -class PopnMusicModernBase(PopnMusicBase, metaclass=ABCMeta): +class PopnMusicModernBase(PopnMusicBase, ABC): # Chart type, as returned from the game GAME_CHART_TYPE_EASY: Final[int] = 0 @@ -1119,7 +1118,7 @@ class PopnMusicModernBase(PopnMusicBase, metaclass=ABCMeta): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() account = request.child('account') if account is not None: diff --git a/bemani/backend/popn/eclale.py b/bemani/backend/popn/eclale.py index d47b491..214e54a 100644 --- a/bemani/backend/popn/eclale.py +++ b/bemani/backend/popn/eclale.py @@ -1,7 +1,7 @@ # vim: set fileencoding=utf-8 import binascii -import copy from typing import Dict, List +from typing_extensions import Final from bemani.backend.popn.base import PopnMusicBase from bemani.backend.popn.lapistoria import PopnMusicLapistoria @@ -13,30 +13,30 @@ from bemani.protocol import Node class PopnMusicEclale(PopnMusicBase): - name = "Pop'n Music éclale" - version = VersionConstants.POPN_MUSIC_ECLALE + name: str = "Pop'n Music éclale" + version: int = VersionConstants.POPN_MUSIC_ECLALE # Chart type, as returned from the game - GAME_CHART_TYPE_EASY = 0 - GAME_CHART_TYPE_NORMAL = 1 - GAME_CHART_TYPE_HYPER = 2 - GAME_CHART_TYPE_EX = 3 + GAME_CHART_TYPE_EASY: Final[int] = 0 + GAME_CHART_TYPE_NORMAL: Final[int] = 1 + GAME_CHART_TYPE_HYPER: Final[int] = 2 + GAME_CHART_TYPE_EX: Final[int] = 3 # Medal type, as returned from the game - GAME_PLAY_MEDAL_CIRCLE_FAILED = 1 - GAME_PLAY_MEDAL_DIAMOND_FAILED = 2 - GAME_PLAY_MEDAL_STAR_FAILED = 3 - GAME_PLAY_MEDAL_EASY_CLEAR = 4 - GAME_PLAY_MEDAL_CIRCLE_CLEARED = 5 - GAME_PLAY_MEDAL_DIAMOND_CLEARED = 6 - GAME_PLAY_MEDAL_STAR_CLEARED = 7 - GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO = 8 - GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO = 9 - GAME_PLAY_MEDAL_STAR_FULL_COMBO = 10 - GAME_PLAY_MEDAL_PERFECT = 11 + GAME_PLAY_MEDAL_CIRCLE_FAILED: Final[int] = 1 + GAME_PLAY_MEDAL_DIAMOND_FAILED: Final[int] = 2 + GAME_PLAY_MEDAL_STAR_FAILED: Final[int] = 3 + GAME_PLAY_MEDAL_EASY_CLEAR: Final[int] = 4 + GAME_PLAY_MEDAL_CIRCLE_CLEARED: Final[int] = 5 + GAME_PLAY_MEDAL_DIAMOND_CLEARED: Final[int] = 6 + GAME_PLAY_MEDAL_STAR_CLEARED: Final[int] = 7 + GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO: Final[int] = 8 + GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO: Final[int] = 9 + GAME_PLAY_MEDAL_STAR_FULL_COMBO: Final[int] = 10 + GAME_PLAY_MEDAL_PERFECT: Final[int] = 11 # Biggest ID in the music DB - GAME_MAX_MUSIC_ID = 1550 + GAME_MAX_MUSIC_ID: Final[int] = 1550 def previous_version(self) -> PopnMusicBase: return PopnMusicLapistoria(self.data, self.config, self.model) @@ -700,7 +700,7 @@ class PopnMusicEclale(PopnMusicBase): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() account = request.child('account') if account is not None: diff --git a/bemani/backend/popn/factory.py b/bemani/backend/popn/factory.py index 166c3ad..0fda189 100644 --- a/bemani/backend/popn/factory.py +++ b/bemani/backend/popn/factory.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import List, Optional, Type from bemani.backend.base import Base, Factory from bemani.backend.popn.stubs import ( @@ -34,7 +34,7 @@ from bemani.data import Config, Data class PopnMusicFactory(Factory): - MANAGED_CLASSES = [ + MANAGED_CLASSES: List[Type[Base]] = [ PopnMusic, PopnMusic2, PopnMusic3, diff --git a/bemani/backend/popn/fantasia.py b/bemani/backend/popn/fantasia.py index 91140dd..3f7e85d 100644 --- a/bemani/backend/popn/fantasia.py +++ b/bemani/backend/popn/fantasia.py @@ -1,6 +1,6 @@ # vim: set fileencoding=utf-8 -import copy from typing import Any, Dict, List +from typing_extensions import Final from bemani.backend.popn.base import PopnMusicBase from bemani.backend.popn.tunestreet import PopnMusicTuneStreet @@ -13,35 +13,35 @@ from bemani.protocol import Node class PopnMusicFantasia(PopnMusicBase): - name = "Pop'n Music fantasia" - version = VersionConstants.POPN_MUSIC_FANTASIA + name: str = "Pop'n Music fantasia" + version: int = VersionConstants.POPN_MUSIC_FANTASIA # Chart type, as returned from the game - GAME_CHART_TYPE_EASY = 2 - GAME_CHART_TYPE_NORMAL = 0 - GAME_CHART_TYPE_HYPER = 1 - GAME_CHART_TYPE_EX = 3 + GAME_CHART_TYPE_EASY: Final[int] = 2 + GAME_CHART_TYPE_NORMAL: Final[int] = 0 + GAME_CHART_TYPE_HYPER: Final[int] = 1 + GAME_CHART_TYPE_EX: Final[int] = 3 # Chart type, as packed into a hiscore binary - GAME_CHART_TYPE_EASY_POSITION = 0 - GAME_CHART_TYPE_NORMAL_POSITION = 1 - GAME_CHART_TYPE_HYPER_POSITION = 2 - GAME_CHART_TYPE_EX_POSITION = 3 + GAME_CHART_TYPE_EASY_POSITION: Final[int] = 0 + GAME_CHART_TYPE_NORMAL_POSITION: Final[int] = 1 + GAME_CHART_TYPE_HYPER_POSITION: Final[int] = 2 + GAME_CHART_TYPE_EX_POSITION: Final[int] = 3 # Medal type, as returned from the game - GAME_PLAY_MEDAL_CIRCLE_FAILED = 1 - GAME_PLAY_MEDAL_DIAMOND_FAILED = 2 - GAME_PLAY_MEDAL_STAR_FAILED = 3 - GAME_PLAY_MEDAL_CIRCLE_CLEARED = 5 - GAME_PLAY_MEDAL_DIAMOND_CLEARED = 6 - GAME_PLAY_MEDAL_STAR_CLEARED = 7 - GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO = 9 - GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO = 10 - GAME_PLAY_MEDAL_STAR_FULL_COMBO = 11 - GAME_PLAY_MEDAL_PERFECT = 15 + GAME_PLAY_MEDAL_CIRCLE_FAILED: Final[int] = 1 + GAME_PLAY_MEDAL_DIAMOND_FAILED: Final[int] = 2 + GAME_PLAY_MEDAL_STAR_FAILED: Final[int] = 3 + GAME_PLAY_MEDAL_CIRCLE_CLEARED: Final[int] = 5 + GAME_PLAY_MEDAL_DIAMOND_CLEARED: Final[int] = 6 + GAME_PLAY_MEDAL_STAR_CLEARED: Final[int] = 7 + GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO: Final[int] = 9 + GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO: Final[int] = 10 + GAME_PLAY_MEDAL_STAR_FULL_COMBO: Final[int] = 11 + GAME_PLAY_MEDAL_PERFECT: Final[int] = 15 # Maximum music ID for this game - GAME_MAX_MUSIC_ID = 1150 + GAME_MAX_MUSIC_ID: Final[int] = 1150 def previous_version(self) -> PopnMusicBase: return PopnMusicTuneStreet(self.data, self.config, self.model) @@ -320,7 +320,7 @@ class PopnMusicFantasia(PopnMusicBase): if request.child_value('is_not_gameover') == 1: return oldprofile - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() newprofile.replace_int('option', request.child_value('option')) newprofile.replace_int('chara', request.child_value('chara')) newprofile.replace_int('mode', request.child_value('mode')) diff --git a/bemani/backend/popn/lapistoria.py b/bemani/backend/popn/lapistoria.py index 9d3e907..ab0c05e 100644 --- a/bemani/backend/popn/lapistoria.py +++ b/bemani/backend/popn/lapistoria.py @@ -1,6 +1,6 @@ # vim: set fileencoding=utf-8 -import copy from typing import Any, Dict, List +from typing_extensions import Final from bemani.backend.popn.base import PopnMusicBase from bemani.backend.popn.sunnypark import PopnMusicSunnyPark @@ -13,31 +13,31 @@ from bemani.protocol import Node class PopnMusicLapistoria(PopnMusicBase): - name = "Pop'n Music ラピストリア" - version = VersionConstants.POPN_MUSIC_LAPISTORIA + name: str = "Pop'n Music ラピストリア" + version: int = VersionConstants.POPN_MUSIC_LAPISTORIA # Chart type, as returned from the game - GAME_CHART_TYPE_EASY = 0 - GAME_CHART_TYPE_NORMAL = 1 - GAME_CHART_TYPE_HYPER = 2 - GAME_CHART_TYPE_EX = 3 + GAME_CHART_TYPE_EASY: Final[int] = 0 + GAME_CHART_TYPE_NORMAL: Final[int] = 1 + GAME_CHART_TYPE_HYPER: Final[int] = 2 + GAME_CHART_TYPE_EX: Final[int] = 3 # Medal type, as returned from the game - GAME_PLAY_MEDAL_NO_PLAY = 0 - GAME_PLAY_MEDAL_CIRCLE_FAILED = 1 - GAME_PLAY_MEDAL_DIAMOND_FAILED = 2 - GAME_PLAY_MEDAL_STAR_FAILED = 3 - GAME_PLAY_MEDAL_EASY_CLEAR = 4 - GAME_PLAY_MEDAL_CIRCLE_CLEARED = 5 - GAME_PLAY_MEDAL_DIAMOND_CLEARED = 6 - GAME_PLAY_MEDAL_STAR_CLEARED = 7 - GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO = 8 - GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO = 9 - GAME_PLAY_MEDAL_STAR_FULL_COMBO = 10 - GAME_PLAY_MEDAL_PERFECT = 11 + GAME_PLAY_MEDAL_NO_PLAY: Final[int] = 0 + GAME_PLAY_MEDAL_CIRCLE_FAILED: Final[int] = 1 + GAME_PLAY_MEDAL_DIAMOND_FAILED: Final[int] = 2 + GAME_PLAY_MEDAL_STAR_FAILED: Final[int] = 3 + GAME_PLAY_MEDAL_EASY_CLEAR: Final[int] = 4 + GAME_PLAY_MEDAL_CIRCLE_CLEARED: Final[int] = 5 + GAME_PLAY_MEDAL_DIAMOND_CLEARED: Final[int] = 6 + GAME_PLAY_MEDAL_STAR_CLEARED: Final[int] = 7 + GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO: Final[int] = 8 + GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO: Final[int] = 9 + GAME_PLAY_MEDAL_STAR_FULL_COMBO: Final[int] = 10 + GAME_PLAY_MEDAL_PERFECT: Final[int] = 11 # Max valud music ID for conversions and stuff - GAME_MAX_MUSIC_ID = 1422 + GAME_MAX_MUSIC_ID: Final[int] = 1422 def previous_version(self) -> PopnMusicBase: return PopnMusicSunnyPark(self.data, self.config, self.model) @@ -766,7 +766,7 @@ class PopnMusicLapistoria(PopnMusicBase): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() account = request.child('account') if account is not None: diff --git a/bemani/backend/popn/peace.py b/bemani/backend/popn/peace.py index 041d7fe..4b8a155 100644 --- a/bemani/backend/popn/peace.py +++ b/bemani/backend/popn/peace.py @@ -9,11 +9,11 @@ from bemani.common import VersionConstants class PopnMusicPeace(PopnMusicModernBase): - name = "Pop'n Music peace" - version = VersionConstants.POPN_MUSIC_PEACE + name: str = "Pop'n Music peace" + version: int = VersionConstants.POPN_MUSIC_PEACE # Biggest ID in the music DB - GAME_MAX_MUSIC_ID = 1877 + GAME_MAX_MUSIC_ID: int = 1877 def previous_version(self) -> PopnMusicBase: return PopnMusicUsaNeko(self.data, self.config, self.model) diff --git a/bemani/backend/popn/stubs.py b/bemani/backend/popn/stubs.py index c27e43f..b3dbcd6 100644 --- a/bemani/backend/popn/stubs.py +++ b/bemani/backend/popn/stubs.py @@ -7,14 +7,14 @@ from bemani.common import VersionConstants class PopnMusic(PopnMusicBase): - name = "Pop'n Music" - version = VersionConstants.POPN_MUSIC + name: str = "Pop'n Music" + version: int = VersionConstants.POPN_MUSIC class PopnMusic2(PopnMusicBase): - name = "Pop'n Music 2" - version = VersionConstants.POPN_MUSIC_2 + name: str = "Pop'n Music 2" + version: int = VersionConstants.POPN_MUSIC_2 def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusic(self.data, self.config, self.model) @@ -22,8 +22,8 @@ class PopnMusic2(PopnMusicBase): class PopnMusic3(PopnMusicBase): - name = "Pop'n Music 3" - version = VersionConstants.POPN_MUSIC_3 + name: str = "Pop'n Music 3" + version: int = VersionConstants.POPN_MUSIC_3 def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusic2(self.data, self.config, self.model) @@ -31,8 +31,8 @@ class PopnMusic3(PopnMusicBase): class PopnMusic4(PopnMusicBase): - name = "Pop'n Music 4" - version = VersionConstants.POPN_MUSIC_4 + name: str = "Pop'n Music 4" + version: int = VersionConstants.POPN_MUSIC_4 def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusic3(self.data, self.config, self.model) @@ -40,8 +40,8 @@ class PopnMusic4(PopnMusicBase): class PopnMusic5(PopnMusicBase): - name = "Pop'n Music 5" - version = VersionConstants.POPN_MUSIC_5 + name: str = "Pop'n Music 5" + version: int = VersionConstants.POPN_MUSIC_5 def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusic4(self.data, self.config, self.model) @@ -49,8 +49,8 @@ class PopnMusic5(PopnMusicBase): class PopnMusic6(PopnMusicBase): - name = "Pop'n Music 6" - version = VersionConstants.POPN_MUSIC_6 + name: str = "Pop'n Music 6" + version: int = VersionConstants.POPN_MUSIC_6 def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusic5(self.data, self.config, self.model) @@ -58,8 +58,8 @@ class PopnMusic6(PopnMusicBase): class PopnMusic7(PopnMusicBase): - name = "Pop'n Music 7" - version = VersionConstants.POPN_MUSIC_7 + name: str = "Pop'n Music 7" + version: int = VersionConstants.POPN_MUSIC_7 def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusic6(self.data, self.config, self.model) @@ -67,8 +67,8 @@ class PopnMusic7(PopnMusicBase): class PopnMusic8(PopnMusicBase): - name = "Pop'n Music 8" - version = VersionConstants.POPN_MUSIC_8 + name: str = "Pop'n Music 8" + version: int = VersionConstants.POPN_MUSIC_8 def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusic7(self.data, self.config, self.model) @@ -76,8 +76,8 @@ class PopnMusic8(PopnMusicBase): class PopnMusic9(PopnMusicBase): - name = "Pop'n Music 9" - version = VersionConstants.POPN_MUSIC_9 + name: str = "Pop'n Music 9" + version: int = VersionConstants.POPN_MUSIC_9 def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusic8(self.data, self.config, self.model) @@ -85,8 +85,8 @@ class PopnMusic9(PopnMusicBase): class PopnMusic10(PopnMusicBase): - name = "Pop'n Music 10" - version = VersionConstants.POPN_MUSIC_10 + name: str = "Pop'n Music 10" + version: int = VersionConstants.POPN_MUSIC_10 def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusic9(self.data, self.config, self.model) @@ -94,8 +94,8 @@ class PopnMusic10(PopnMusicBase): class PopnMusic11(PopnMusicBase): - name = "Pop'n Music 11" - version = VersionConstants.POPN_MUSIC_11 + name: str = "Pop'n Music 11" + version: int = VersionConstants.POPN_MUSIC_11 def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusic10(self.data, self.config, self.model) @@ -103,8 +103,8 @@ class PopnMusic11(PopnMusicBase): class PopnMusicIroha(PopnMusicBase): - name = "Pop'n Music いろは" - version = VersionConstants.POPN_MUSIC_IROHA + name: str = "Pop'n Music いろは" + version: int = VersionConstants.POPN_MUSIC_IROHA def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusic11(self.data, self.config, self.model) @@ -112,8 +112,8 @@ class PopnMusicIroha(PopnMusicBase): class PopnMusicCarnival(PopnMusicBase): - name = "Pop'n Music カーニバル" - version = VersionConstants.POPN_MUSIC_CARNIVAL + name: str = "Pop'n Music カーニバル" + version: int = VersionConstants.POPN_MUSIC_CARNIVAL def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusicIroha(self.data, self.config, self.model) @@ -121,8 +121,8 @@ class PopnMusicCarnival(PopnMusicBase): class PopnMusicFever(PopnMusicBase): - name = "Pop'n Music FEVER!" - version = VersionConstants.POPN_MUSIC_FEVER + name: str = "Pop'n Music FEVER!" + version: int = VersionConstants.POPN_MUSIC_FEVER def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusicCarnival(self.data, self.config, self.model) @@ -130,8 +130,8 @@ class PopnMusicFever(PopnMusicBase): class PopnMusicAdventure(PopnMusicBase): - name = "Pop'n Music ADVENTURE" - version = VersionConstants.POPN_MUSIC_ADVENTURE + name: str = "Pop'n Music ADVENTURE" + version: int = VersionConstants.POPN_MUSIC_ADVENTURE def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusicFever(self.data, self.config, self.model) @@ -139,8 +139,8 @@ class PopnMusicAdventure(PopnMusicBase): class PopnMusicParty(PopnMusicBase): - name = "Pop'n Music Party♪" - version = VersionConstants.POPN_MUSIC_PARTY + name: str = "Pop'n Music Party♪" + version: int = VersionConstants.POPN_MUSIC_PARTY def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusicAdventure(self.data, self.config, self.model) @@ -148,8 +148,8 @@ class PopnMusicParty(PopnMusicBase): class PopnMusicTheMovie(PopnMusicBase): - name = "Pop'n Music THE MOVIE" - version = VersionConstants.POPN_MUSIC_THE_MOVIE + name: str = "Pop'n Music THE MOVIE" + version: int = VersionConstants.POPN_MUSIC_THE_MOVIE def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusicParty(self.data, self.config, self.model) @@ -157,8 +157,8 @@ class PopnMusicTheMovie(PopnMusicBase): class PopnMusicSengokuRetsuden(PopnMusicBase): - name = "Pop'n Music せんごく列伝" - version = VersionConstants.POPN_MUSIC_SENGOKU_RETSUDEN + name: str = "Pop'n Music せんごく列伝" + version: int = VersionConstants.POPN_MUSIC_SENGOKU_RETSUDEN def previous_version(self) -> Optional[PopnMusicBase]: return PopnMusicTheMovie(self.data, self.config, self.model) diff --git a/bemani/backend/popn/sunnypark.py b/bemani/backend/popn/sunnypark.py index c6a2bba..0729d2a 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 Any, Dict, List +from typing_extensions import Final from bemani.backend.popn.base import PopnMusicBase from bemani.backend.popn.fantasia import PopnMusicFantasia @@ -13,35 +13,35 @@ from bemani.protocol import Node class PopnMusicSunnyPark(PopnMusicBase): - name = "Pop'n Music Sunny Park" - version = VersionConstants.POPN_MUSIC_SUNNY_PARK + name: str = "Pop'n Music Sunny Park" + version: int = VersionConstants.POPN_MUSIC_SUNNY_PARK # Chart type, as returned from the game - GAME_CHART_TYPE_EASY = 0 - GAME_CHART_TYPE_NORMAL = 1 - GAME_CHART_TYPE_HYPER = 2 - GAME_CHART_TYPE_EX = 3 + GAME_CHART_TYPE_EASY: Final[int] = 0 + GAME_CHART_TYPE_NORMAL: Final[int] = 1 + GAME_CHART_TYPE_HYPER: Final[int] = 2 + GAME_CHART_TYPE_EX: Final[int] = 3 # Chart type, as packed into a hiscore binary - GAME_CHART_TYPE_EASY_POSITION = 0 - GAME_CHART_TYPE_NORMAL_POSITION = 1 - GAME_CHART_TYPE_HYPER_POSITION = 2 - GAME_CHART_TYPE_EX_POSITION = 3 + GAME_CHART_TYPE_EASY_POSITION: Final[int] = 0 + GAME_CHART_TYPE_NORMAL_POSITION: Final[int] = 1 + GAME_CHART_TYPE_HYPER_POSITION: Final[int] = 2 + GAME_CHART_TYPE_EX_POSITION: Final[int] = 3 # Medal type, as returned from the game - GAME_PLAY_MEDAL_CIRCLE_FAILED = 1 - GAME_PLAY_MEDAL_DIAMOND_FAILED = 2 - GAME_PLAY_MEDAL_STAR_FAILED = 3 - GAME_PLAY_MEDAL_CIRCLE_CLEARED = 5 - GAME_PLAY_MEDAL_DIAMOND_CLEARED = 6 - GAME_PLAY_MEDAL_STAR_CLEARED = 7 - GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO = 9 - GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO = 10 - GAME_PLAY_MEDAL_STAR_FULL_COMBO = 11 - GAME_PLAY_MEDAL_PERFECT = 15 + GAME_PLAY_MEDAL_CIRCLE_FAILED: Final[int] = 1 + GAME_PLAY_MEDAL_DIAMOND_FAILED: Final[int] = 2 + GAME_PLAY_MEDAL_STAR_FAILED: Final[int] = 3 + GAME_PLAY_MEDAL_CIRCLE_CLEARED: Final[int] = 5 + GAME_PLAY_MEDAL_DIAMOND_CLEARED: Final[int] = 6 + GAME_PLAY_MEDAL_STAR_CLEARED: Final[int] = 7 + GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO: Final[int] = 9 + GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO: Final[int] = 10 + GAME_PLAY_MEDAL_STAR_FULL_COMBO: Final[int] = 11 + GAME_PLAY_MEDAL_PERFECT: Final[int] = 15 # Maximum music ID for this game - GAME_MAX_MUSIC_ID = 1350 + GAME_MAX_MUSIC_ID: Final[int] = 1350 def previous_version(self) -> PopnMusicBase: return PopnMusicFantasia(self.data, self.config, self.model) @@ -360,7 +360,7 @@ class PopnMusicSunnyPark(PopnMusicBase): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() newprofile.replace_int('option', request.child_value('option')) newprofile.replace_int('chara', request.child_value('chara')) newprofile.replace_int('mode', request.child_value('mode')) diff --git a/bemani/backend/popn/tunestreet.py b/bemani/backend/popn/tunestreet.py index 7237774..c4059fa 100644 --- a/bemani/backend/popn/tunestreet.py +++ b/bemani/backend/popn/tunestreet.py @@ -1,6 +1,6 @@ # vim: set fileencoding=utf-8 -import copy from typing import Dict, Any +from typing_extensions import Final from bemani.backend.popn.base import PopnMusicBase from bemani.backend.popn.stubs import PopnMusicSengokuRetsuden @@ -13,49 +13,49 @@ from bemani.protocol import Node class PopnMusicTuneStreet(PopnMusicBase): - name = "Pop'n Music TUNE STREET" - version = VersionConstants.POPN_MUSIC_TUNE_STREET + name: str = "Pop'n Music TUNE STREET" + version: int = VersionConstants.POPN_MUSIC_TUNE_STREET # Play modes, as reported by profile save from the game - GAME_PLAY_MODE_CHALLENGE = 3 - GAME_PLAY_MODE_CHO_CHALLENGE = 4 - GAME_PLAY_MODE_TOWN_CHO_CHALLENGE = 15 + GAME_PLAY_MODE_CHALLENGE: Final[int] = 3 + GAME_PLAY_MODE_CHO_CHALLENGE: Final[int] = 4 + GAME_PLAY_MODE_TOWN_CHO_CHALLENGE: Final[int] = 15 # Play flags, as saved into/loaded from the DB - GAME_PLAY_FLAG_FAILED = 0 - GAME_PLAY_FLAG_CLEARED = 1 - GAME_PLAY_FLAG_FULL_COMBO = 2 - GAME_PLAY_FLAG_PERFECT_COMBO = 3 + GAME_PLAY_FLAG_FAILED: Final[int] = 0 + GAME_PLAY_FLAG_CLEARED: Final[int] = 1 + GAME_PLAY_FLAG_FULL_COMBO: Final[int] = 2 + GAME_PLAY_FLAG_PERFECT_COMBO: Final[int] = 3 # Chart type, as reported by profile save from the game - GAME_CHART_TYPE_NORMAL = 0 - GAME_CHART_TYPE_HYPER = 1 - GAME_CHART_TYPE_5_BUTTON = 2 - GAME_CHART_TYPE_EX = 3 - GAME_CHART_TYPE_BATTLE_NORMAL = 4 - GAME_CHART_TYPE_BATTLE_HYPER = 5 - GAME_CHART_TYPE_ENJOY_5_BUTTON = 6 - GAME_CHART_TYPE_ENJOY_9_BUTTON = 7 + GAME_CHART_TYPE_NORMAL: Final[int] = 0 + GAME_CHART_TYPE_HYPER: Final[int] = 1 + GAME_CHART_TYPE_5_BUTTON: Final[int] = 2 + GAME_CHART_TYPE_EX: Final[int] = 3 + GAME_CHART_TYPE_BATTLE_NORMAL: Final[int] = 4 + GAME_CHART_TYPE_BATTLE_HYPER: Final[int] = 5 + GAME_CHART_TYPE_ENJOY_5_BUTTON: Final[int] = 6 + GAME_CHART_TYPE_ENJOY_9_BUTTON: Final[int] = 7 # Extra chart types supported by Pop'n 19 - CHART_TYPE_OLD_NORMAL = 4 - CHART_TYPE_OLD_HYPER = 5 - CHART_TYPE_OLD_EX = 6 - CHART_TYPE_ENJOY_5_BUTTON = 7 - CHART_TYPE_ENJOY_9_BUTTON = 8 - CHART_TYPE_5_BUTTON = 9 + CHART_TYPE_OLD_NORMAL: Final[int] = 4 + CHART_TYPE_OLD_HYPER: Final[int] = 5 + CHART_TYPE_OLD_EX: Final[int] = 6 + CHART_TYPE_ENJOY_5_BUTTON: Final[int] = 7 + CHART_TYPE_ENJOY_9_BUTTON: Final[int] = 8 + CHART_TYPE_5_BUTTON: Final[int] = 9 # Chart type, as packed into a hiscore binary - GAME_CHART_TYPE_5_BUTTON_POSITION = 0 - GAME_CHART_TYPE_NORMAL_POSITION = 1 - GAME_CHART_TYPE_HYPER_POSITION = 2 - GAME_CHART_TYPE_EX_POSITION = 3 - GAME_CHART_TYPE_CHO_NORMAL_POSITION = 4 - GAME_CHART_TYPE_CHO_HYPER_POSITION = 5 - GAME_CHART_TYPE_CHO_EX_POSITION = 6 + GAME_CHART_TYPE_5_BUTTON_POSITION: Final[int] = 0 + GAME_CHART_TYPE_NORMAL_POSITION: Final[int] = 1 + GAME_CHART_TYPE_HYPER_POSITION: Final[int] = 2 + GAME_CHART_TYPE_EX_POSITION: Final[int] = 3 + GAME_CHART_TYPE_CHO_NORMAL_POSITION: Final[int] = 4 + GAME_CHART_TYPE_CHO_HYPER_POSITION: Final[int] = 5 + GAME_CHART_TYPE_CHO_EX_POSITION: Final[int] = 6 # Highest song ID we can represent - GAME_MAX_MUSIC_ID = 1045 + GAME_MAX_MUSIC_ID: Final[int] = 1045 def previous_version(self) -> PopnMusicBase: return PopnMusicSengokuRetsuden(self.data, self.config, self.model) @@ -351,7 +351,7 @@ class PopnMusicTuneStreet(PopnMusicBase): return root def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() # Extract the playmode, important for scores later playmode = int(request.attribute('play_mode')) diff --git a/bemani/backend/popn/usaneko.py b/bemani/backend/popn/usaneko.py index e761f7f..aa691fc 100644 --- a/bemani/backend/popn/usaneko.py +++ b/bemani/backend/popn/usaneko.py @@ -9,11 +9,11 @@ from bemani.common import VersionConstants class PopnMusicUsaNeko(PopnMusicModernBase): - name = "Pop'n Music うさぎと猫と少年の夢" - version = VersionConstants.POPN_MUSIC_USANEKO + name: str = "Pop'n Music うさぎと猫と少年の夢" + version: int = VersionConstants.POPN_MUSIC_USANEKO # Biggest ID in the music DB - GAME_MAX_MUSIC_ID = 1704 + GAME_MAX_MUSIC_ID: int = 1704 def previous_version(self) -> PopnMusicBase: return PopnMusicEclale(self.data, self.config, self.model) diff --git a/bemani/backend/reflec/base.py b/bemani/backend/reflec/base.py index c0379c5..cda785f 100644 --- a/bemani/backend/reflec/base.py +++ b/bemani/backend/reflec/base.py @@ -1,5 +1,6 @@ # vim: set fileencoding=utf-8 from typing import Dict, List, Optional +from typing_extensions import Final from bemani.backend.base import Base from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler @@ -13,26 +14,26 @@ class ReflecBeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): Base game class for all Reflec Beat version that we support. """ - game = GameConstants.REFLEC_BEAT + game: GameConstants = GameConstants.REFLEC_BEAT # Chart types, as stored in the DB - CHART_TYPE_BASIC = 0 - CHART_TYPE_MEDIUM = 1 - CHART_TYPE_HARD = 2 - CHART_TYPE_SPECIAL = 3 + CHART_TYPE_BASIC: Final[int] = 0 + CHART_TYPE_MEDIUM: Final[int] = 1 + CHART_TYPE_HARD: Final[int] = 2 + CHART_TYPE_SPECIAL: Final[int] = 3 # Clear types, as saved/loaded from the DB - CLEAR_TYPE_NO_PLAY = DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY - CLEAR_TYPE_FAILED = DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED - CLEAR_TYPE_CLEARED = DBConstants.REFLEC_BEAT_CLEAR_TYPE_CLEARED - CLEAR_TYPE_HARD_CLEARED = DBConstants.REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED - CLEAR_TYPE_S_HARD_CLEARED = DBConstants.REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED + CLEAR_TYPE_NO_PLAY: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY + CLEAR_TYPE_FAILED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED + CLEAR_TYPE_CLEARED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_CLEARED + CLEAR_TYPE_HARD_CLEARED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED + CLEAR_TYPE_S_HARD_CLEARED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED # Combo types, as saved/loaded from the DB - COMBO_TYPE_NONE = DBConstants.REFLEC_BEAT_COMBO_TYPE_NONE - COMBO_TYPE_ALMOST_COMBO = DBConstants.REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO - COMBO_TYPE_FULL_COMBO = DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO - COMBO_TYPE_FULL_COMBO_ALL_JUST = DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST + COMBO_TYPE_NONE: Final[int] = DBConstants.REFLEC_BEAT_COMBO_TYPE_NONE + COMBO_TYPE_ALMOST_COMBO: Final[int] = DBConstants.REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO + COMBO_TYPE_FULL_COMBO: Final[int] = DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO + COMBO_TYPE_FULL_COMBO_ALL_JUST: Final[int] = DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST # Return the local2 and lobby2 service so that matching will work on newer # Reflec Beat games. diff --git a/bemani/backend/reflec/colette.py b/bemani/backend/reflec/colette.py index db86b08..44ad4ac 100644 --- a/bemani/backend/reflec/colette.py +++ b/bemani/backend/reflec/colette.py @@ -1,5 +1,5 @@ -import copy from typing import Optional, Dict, List, Tuple, Any +from typing_extensions import Final from bemani.backend.reflec.base import ReflecBeatBase from bemani.backend.reflec.limelight import ReflecBeatLimelight @@ -11,15 +11,15 @@ from bemani.protocol import Node class ReflecBeatColette(ReflecBeatBase): - name = "REFLEC BEAT colette" - version = VersionConstants.REFLEC_BEAT_COLETTE + name: str = "REFLEC BEAT colette" + version: int = VersionConstants.REFLEC_BEAT_COLETTE # Clear types according to the game - GAME_CLEAR_TYPE_NO_PLAY = 0 - GAME_CLEAR_TYPE_FAILED = 1 - GAME_CLEAR_TYPE_CLEARED = 2 - GAME_CLEAR_TYPE_ALMOST_COMBO = 3 - GAME_CLEAR_TYPE_FULL_COMBO = 4 + GAME_CLEAR_TYPE_NO_PLAY: Final[int] = 0 + GAME_CLEAR_TYPE_FAILED: Final[int] = 1 + GAME_CLEAR_TYPE_CLEARED: Final[int] = 2 + GAME_CLEAR_TYPE_ALMOST_COMBO: Final[int] = 3 + GAME_CLEAR_TYPE_FULL_COMBO: Final[int] = 4 def previous_version(self) -> Optional[ReflecBeatBase]: return ReflecBeatLimelight(self.data, self.config, self.model) @@ -941,7 +941,7 @@ class ReflecBeatColette(ReflecBeatBase): def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: game_config = self.get_game_config() - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() newprofile.replace_int('lid', ID.parse_machine_id(request.child_value('pdata/account/lid'))) newprofile.replace_str('name', request.child_value('pdata/base/name')) diff --git a/bemani/backend/reflec/groovin.py b/bemani/backend/reflec/groovin.py index df3e68d..cfacafa 100644 --- a/bemani/backend/reflec/groovin.py +++ b/bemani/backend/reflec/groovin.py @@ -1,5 +1,5 @@ -import copy from typing import Optional, Dict, Any, List, Tuple +from typing_extensions import Final from bemani.backend.reflec.base import ReflecBeatBase from bemani.backend.reflec.colette import ReflecBeatColette @@ -11,24 +11,24 @@ from bemani.protocol import Node class ReflecBeatGroovin(ReflecBeatBase): - name = "REFLEC BEAT groovin'!!" - version = VersionConstants.REFLEC_BEAT_GROOVIN + name: str = "REFLEC BEAT groovin'!!" + version: int = VersionConstants.REFLEC_BEAT_GROOVIN # Clear types according to the game - GAME_CLEAR_TYPE_NO_PLAY = 0 - GAME_CLEAR_TYPE_EARLY_FAILED = 1 - GAME_CLEAR_TYPE_FAILED = 2 - GAME_CLEAR_TYPE_CLEARED = 9 - GAME_CLEAR_TYPE_HARD_CLEARED = 10 - GAME_CLEAR_TYPE_S_HARD_CLEARED = 11 + GAME_CLEAR_TYPE_NO_PLAY: Final[int] = 0 + GAME_CLEAR_TYPE_EARLY_FAILED: Final[int] = 1 + GAME_CLEAR_TYPE_FAILED: Final[int] = 2 + GAME_CLEAR_TYPE_CLEARED: Final[int] = 9 + GAME_CLEAR_TYPE_HARD_CLEARED: Final[int] = 10 + GAME_CLEAR_TYPE_S_HARD_CLEARED: Final[int] = 11 # Combo types according to the game (actually a bitmask, where bit 0 is # full combo status, and bit 2 is just reflec status). But we don't support # saving just reflec without full combo, so we downgrade it. - GAME_COMBO_TYPE_NONE = 0 - GAME_COMBO_TYPE_ALL_JUST = 2 - GAME_COMBO_TYPE_FULL_COMBO = 1 - GAME_COMBO_TYPE_FULL_COMBO_ALL_JUST = 3 + GAME_COMBO_TYPE_NONE: Final[int] = 0 + GAME_COMBO_TYPE_ALL_JUST: Final[int] = 2 + GAME_COMBO_TYPE_FULL_COMBO: Final[int] = 1 + GAME_COMBO_TYPE_FULL_COMBO_ALL_JUST: Final[int] = 3 def previous_version(self) -> Optional[ReflecBeatBase]: return ReflecBeatColette(self.data, self.config, self.model) @@ -1207,7 +1207,7 @@ class ReflecBeatGroovin(ReflecBeatBase): def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: game_config = self.get_game_config() - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() # Save base player profile info newprofile.replace_int('lid', ID.parse_machine_id(request.child_value('pdata/account/lid'))) diff --git a/bemani/backend/reflec/limelight.py b/bemani/backend/reflec/limelight.py index 7f34612..23f2c01 100644 --- a/bemani/backend/reflec/limelight.py +++ b/bemani/backend/reflec/limelight.py @@ -1,5 +1,5 @@ -import copy from typing import Optional, Dict, Any, Tuple +from typing_extensions import Final from bemani.backend.reflec.base import ReflecBeatBase from bemani.backend.reflec.reflecbeat import ReflecBeat @@ -11,14 +11,14 @@ from bemani.protocol import Node class ReflecBeatLimelight(ReflecBeatBase): - name = "REFLEC BEAT limelight" - version = VersionConstants.REFLEC_BEAT_LIMELIGHT + name: str = "REFLEC BEAT limelight" + version: int = VersionConstants.REFLEC_BEAT_LIMELIGHT # Clear types according to the game - GAME_CLEAR_TYPE_NO_PLAY = 0 - GAME_CLEAR_TYPE_FAILED = 2 - GAME_CLEAR_TYPE_CLEARED = 3 - GAME_CLEAR_TYPE_FULL_COMBO = 4 + GAME_CLEAR_TYPE_NO_PLAY: Final[int] = 0 + GAME_CLEAR_TYPE_FAILED: Final[int] = 2 + GAME_CLEAR_TYPE_CLEARED: Final[int] = 3 + GAME_CLEAR_TYPE_FULL_COMBO: Final[int] = 4 def previous_version(self) -> Optional[ReflecBeatBase]: return ReflecBeat(self.data, self.config, self.model) @@ -738,7 +738,7 @@ class ReflecBeatLimelight(ReflecBeatBase): def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: game_config = self.get_game_config() - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() newprofile.replace_int('lid', ID.parse_machine_id(request.child_value('lid'))) newprofile.replace_str('name', request.child_value('pdata/base/name')) diff --git a/bemani/backend/reflec/reflecbeat.py b/bemani/backend/reflec/reflecbeat.py index dffcc39..38a91b0 100644 --- a/bemani/backend/reflec/reflecbeat.py +++ b/bemani/backend/reflec/reflecbeat.py @@ -1,5 +1,5 @@ -import copy from typing import Any, Dict, Tuple +from typing_extensions import Final from bemani.backend.reflec.base import ReflecBeatBase @@ -10,13 +10,13 @@ from bemani.protocol import Node class ReflecBeat(ReflecBeatBase): - name = "REFLEC BEAT" - version = VersionConstants.REFLEC_BEAT + name: str = "REFLEC BEAT" + version: int = VersionConstants.REFLEC_BEAT # Clear types according to the game - GAME_CLEAR_TYPE_NO_PLAY = 0 - GAME_CLEAR_TYPE_PLAYED = 2 - GAME_CLEAR_TYPE_FULL_COMBO = 3 + GAME_CLEAR_TYPE_NO_PLAY: Final[int] = 0 + GAME_CLEAR_TYPE_PLAYED: Final[int] = 2 + GAME_CLEAR_TYPE_FULL_COMBO: Final[int] = 3 @classmethod def get_settings(cls) -> Dict[str, Any]: @@ -399,7 +399,7 @@ class ReflecBeat(ReflecBeatBase): def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: game_config = self.get_game_config() - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() newprofile.replace_int('lid', ID.parse_machine_id(request.child_value('lid'))) newprofile.replace_str('name', request.child_value('pdata/base/name')) diff --git a/bemani/backend/reflec/volzza.py b/bemani/backend/reflec/volzza.py index a577f62..d377277 100644 --- a/bemani/backend/reflec/volzza.py +++ b/bemani/backend/reflec/volzza.py @@ -1,4 +1,3 @@ -import copy from typing import Any, Dict, List, Optional from bemani.backend.reflec.base import ReflecBeatBase @@ -12,8 +11,8 @@ from bemani.protocol import Node class ReflecBeatVolzza(ReflecBeatVolzzaBase): - name = "REFLEC BEAT VOLZZA" - version = VersionConstants.REFLEC_BEAT_VOLZZA + name: str = "REFLEC BEAT VOLZZA" + version: int = VersionConstants.REFLEC_BEAT_VOLZZA def previous_version(self) -> Optional[ReflecBeatBase]: return ReflecBeatGroovin(self.data, self.config, self.model) @@ -544,7 +543,7 @@ class ReflecBeatVolzza(ReflecBeatVolzzaBase): def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: game_config = self.get_game_config() - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() # Save base player profile info newprofile.replace_int('lid', ID.parse_machine_id(request.child_value('pdata/account/lid'))) diff --git a/bemani/backend/reflec/volzza2.py b/bemani/backend/reflec/volzza2.py index 9189716..fe20f9b 100644 --- a/bemani/backend/reflec/volzza2.py +++ b/bemani/backend/reflec/volzza2.py @@ -1,4 +1,3 @@ -import copy from typing import Any, Dict, List, Optional from bemani.backend.reflec.base import ReflecBeatBase @@ -12,8 +11,8 @@ from bemani.protocol import Node class ReflecBeatVolzza2(ReflecBeatVolzzaBase): - name = "REFLEC BEAT VOLZZA 2" - version = VersionConstants.REFLEC_BEAT_VOLZZA_2 + name: str = "REFLEC BEAT VOLZZA 2" + version: int = VersionConstants.REFLEC_BEAT_VOLZZA_2 def previous_version(self) -> Optional[ReflecBeatBase]: return ReflecBeatVolzza(self.data, self.config, self.model) @@ -655,7 +654,7 @@ class ReflecBeatVolzza2(ReflecBeatVolzzaBase): def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: game_config = self.get_game_config() - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() # Save base player profile info newprofile.replace_int('lid', ID.parse_machine_id(request.child_value('pdata/account/lid'))) diff --git a/bemani/backend/reflec/volzzabase.py b/bemani/backend/reflec/volzzabase.py index daba2f7..fc8f98c 100644 --- a/bemani/backend/reflec/volzzabase.py +++ b/bemani/backend/reflec/volzzabase.py @@ -1,4 +1,5 @@ from typing import Dict, List, Tuple +from typing_extensions import Final from bemani.backend.reflec.base import ReflecBeatBase @@ -10,20 +11,20 @@ from bemani.protocol import Node class ReflecBeatVolzzaBase(ReflecBeatBase): # Clear types according to the game - GAME_CLEAR_TYPE_NO_PLAY = 0 - GAME_CLEAR_TYPE_EARLY_FAILED = 1 - GAME_CLEAR_TYPE_FAILED = 2 - GAME_CLEAR_TYPE_CLEARED = 9 - GAME_CLEAR_TYPE_HARD_CLEARED = 10 - GAME_CLEAR_TYPE_S_HARD_CLEARED = 11 + GAME_CLEAR_TYPE_NO_PLAY: Final[int] = 0 + GAME_CLEAR_TYPE_EARLY_FAILED: Final[int] = 1 + GAME_CLEAR_TYPE_FAILED: Final[int] = 2 + GAME_CLEAR_TYPE_CLEARED: Final[int] = 9 + GAME_CLEAR_TYPE_HARD_CLEARED: Final[int] = 10 + GAME_CLEAR_TYPE_S_HARD_CLEARED: Final[int] = 11 # Combo types according to the game (actually a bitmask, where bit 0 is # full combo status, and bit 2 is just reflec status). But we don't support # saving just reflec without full combo, so we downgrade it. - GAME_COMBO_TYPE_NONE = 0 - GAME_COMBO_TYPE_ALL_JUST = 2 - GAME_COMBO_TYPE_FULL_COMBO = 1 - GAME_COMBO_TYPE_FULL_COMBO_ALL_JUST = 3 + GAME_COMBO_TYPE_NONE: Final[int] = 0 + GAME_COMBO_TYPE_ALL_JUST: Final[int] = 2 + GAME_COMBO_TYPE_FULL_COMBO: Final[int] = 1 + GAME_COMBO_TYPE_FULL_COMBO_ALL_JUST: Final[int] = 3 def _db_to_game_clear_type(self, db_status: int) -> int: return { diff --git a/bemani/backend/sdvx/base.py b/bemani/backend/sdvx/base.py index 02b1e33..eda58d4 100644 --- a/bemani/backend/sdvx/base.py +++ b/bemani/backend/sdvx/base.py @@ -1,5 +1,6 @@ # vim: set fileencoding=utf-8 from typing import Dict, Optional +from typing_extensions import Final from bemani.backend.base import Base from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler @@ -13,32 +14,32 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): Base game class for all Sound Voltex version that we support. """ - game = GameConstants.SDVX + game: GameConstants = GameConstants.SDVX - CLEAR_TYPE_NO_PLAY = DBConstants.SDVX_CLEAR_TYPE_NO_PLAY - CLEAR_TYPE_FAILED = DBConstants.SDVX_CLEAR_TYPE_FAILED - CLEAR_TYPE_CLEAR = DBConstants.SDVX_CLEAR_TYPE_CLEAR - CLEAR_TYPE_HARD_CLEAR = DBConstants.SDVX_CLEAR_TYPE_HARD_CLEAR - CLEAR_TYPE_ULTIMATE_CHAIN = DBConstants.SDVX_CLEAR_TYPE_ULTIMATE_CHAIN - CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN = DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN + CLEAR_TYPE_NO_PLAY: Final[int] = DBConstants.SDVX_CLEAR_TYPE_NO_PLAY + CLEAR_TYPE_FAILED: Final[int] = DBConstants.SDVX_CLEAR_TYPE_FAILED + CLEAR_TYPE_CLEAR: Final[int] = DBConstants.SDVX_CLEAR_TYPE_CLEAR + CLEAR_TYPE_HARD_CLEAR: Final[int] = DBConstants.SDVX_CLEAR_TYPE_HARD_CLEAR + CLEAR_TYPE_ULTIMATE_CHAIN: Final[int] = DBConstants.SDVX_CLEAR_TYPE_ULTIMATE_CHAIN + CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: Final[int] = DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN - GRADE_NO_PLAY = DBConstants.SDVX_GRADE_NO_PLAY - GRADE_D = DBConstants.SDVX_GRADE_D - GRADE_C = DBConstants.SDVX_GRADE_C - GRADE_B = DBConstants.SDVX_GRADE_B - GRADE_A = DBConstants.SDVX_GRADE_A - GRADE_A_PLUS = DBConstants.SDVX_GRADE_A_PLUS - GRADE_AA = DBConstants.SDVX_GRADE_AA - GRADE_AA_PLUS = DBConstants.SDVX_GRADE_AA_PLUS - GRADE_AAA = DBConstants.SDVX_GRADE_AAA - GRADE_AAA_PLUS = DBConstants.SDVX_GRADE_AAA_PLUS - GRADE_S = DBConstants.SDVX_GRADE_S + GRADE_NO_PLAY: Final[int] = DBConstants.SDVX_GRADE_NO_PLAY + GRADE_D: Final[int] = DBConstants.SDVX_GRADE_D + GRADE_C: Final[int] = DBConstants.SDVX_GRADE_C + GRADE_B: Final[int] = DBConstants.SDVX_GRADE_B + GRADE_A: Final[int] = DBConstants.SDVX_GRADE_A + GRADE_A_PLUS: Final[int] = DBConstants.SDVX_GRADE_A_PLUS + GRADE_AA: Final[int] = DBConstants.SDVX_GRADE_AA + GRADE_AA_PLUS: Final[int] = DBConstants.SDVX_GRADE_AA_PLUS + GRADE_AAA: Final[int] = DBConstants.SDVX_GRADE_AAA + GRADE_AAA_PLUS: Final[int] = DBConstants.SDVX_GRADE_AAA_PLUS + GRADE_S: Final[int] = DBConstants.SDVX_GRADE_S - CHART_TYPE_NOVICE = 0 - CHART_TYPE_ADVANCED = 1 - CHART_TYPE_EXHAUST = 2 - CHART_TYPE_INFINITE = 3 - CHART_TYPE_MAXIMUM = 4 + CHART_TYPE_NOVICE: Final[int] = 0 + CHART_TYPE_ADVANCED: Final[int] = 1 + CHART_TYPE_EXHAUST: Final[int] = 2 + CHART_TYPE_INFINITE: Final[int] = 3 + CHART_TYPE_MAXIMUM: Final[int] = 4 def previous_version(self) -> Optional['SoundVoltexBase']: """ diff --git a/bemani/backend/sdvx/booth.py b/bemani/backend/sdvx/booth.py index bbf0f1d..039abdd 100644 --- a/bemani/backend/sdvx/booth.py +++ b/bemani/backend/sdvx/booth.py @@ -1,6 +1,6 @@ # vim: set fileencoding=utf-8 -import copy from typing import Any, Dict, Optional, Tuple +from typing_extensions import Final from bemani.backend.ess import EventLogHandler from bemani.backend.sdvx.base import SoundVoltexBase @@ -14,27 +14,27 @@ class SoundVoltexBooth( SoundVoltexBase, ): - name = 'SOUND VOLTEX BOOTH' - version = VersionConstants.SDVX_BOOTH + name: str = 'SOUND VOLTEX BOOTH' + version: int = VersionConstants.SDVX_BOOTH - GAME_LIMITED_LOCKED = 1 - GAME_LIMITED_UNLOCKED = 2 + GAME_LIMITED_LOCKED: Final[int] = 1 + GAME_LIMITED_UNLOCKED: Final[int] = 2 - GAME_CURRENCY_PACKETS = 0 - GAME_CURRENCY_BLOCKS = 1 + GAME_CURRENCY_PACKETS: Final[int] = 0 + GAME_CURRENCY_BLOCKS: Final[int] = 1 - GAME_CLEAR_TYPE_NO_CLEAR = 1 - GAME_CLEAR_TYPE_CLEAR = 2 - GAME_CLEAR_TYPE_ULTIMATE_CHAIN = 3 - GAME_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN = 4 + GAME_CLEAR_TYPE_NO_CLEAR: Final[int] = 1 + GAME_CLEAR_TYPE_CLEAR: Final[int] = 2 + GAME_CLEAR_TYPE_ULTIMATE_CHAIN: Final[int] = 3 + GAME_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: Final[int] = 4 - GAME_GRADE_NO_PLAY = 0 - GAME_GRADE_D = 1 - GAME_GRADE_C = 2 - GAME_GRADE_B = 3 - GAME_GRADE_A = 4 - GAME_GRADE_AA = 5 - GAME_GRADE_AAA = 6 + GAME_GRADE_NO_PLAY: Final[int] = 0 + GAME_GRADE_D: Final[int] = 1 + GAME_GRADE_C: Final[int] = 2 + GAME_GRADE_B: Final[int] = 3 + GAME_GRADE_A: Final[int] = 4 + GAME_GRADE_AA: Final[int] = 5 + GAME_GRADE_AAA: Final[int] = 6 @classmethod def get_settings(cls) -> Dict[str, Any]: @@ -470,7 +470,7 @@ class SoundVoltexBooth( return game def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() # Update experience and in-game currencies earned_gamecoin_packet = request.child_value('earned_gamecoin_packet') diff --git a/bemani/backend/sdvx/factory.py b/bemani/backend/sdvx/factory.py index ea3bd32..3f2f6ec 100644 --- a/bemani/backend/sdvx/factory.py +++ b/bemani/backend/sdvx/factory.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import List, Optional, Type from bemani.backend.base import Base, Factory from bemani.backend.sdvx.booth import SoundVoltexBooth @@ -13,7 +13,7 @@ from bemani.data import Config, Data class SoundVoltexFactory(Factory): - MANAGED_CLASSES = [ + MANAGED_CLASSES: List[Type[Base]] = [ SoundVoltexBooth, SoundVoltexInfiniteInfection, SoundVoltexGravityWars, diff --git a/bemani/backend/sdvx/gravitywars.py b/bemani/backend/sdvx/gravitywars.py index bf404fd..99476ea 100644 --- a/bemani/backend/sdvx/gravitywars.py +++ b/bemani/backend/sdvx/gravitywars.py @@ -1,5 +1,6 @@ # vim: set fileencoding=utf-8 from typing import Any, Dict, List, Optional +from typing_extensions import Final from bemani.backend.ess import EventLogHandler from bemani.backend.sdvx.base import SoundVoltexBase @@ -13,35 +14,35 @@ class SoundVoltexGravityWars( SoundVoltexBase, ): - name = 'SOUND VOLTEX III GRAVITY WARS' - version = VersionConstants.SDVX_GRAVITY_WARS + name: str = 'SOUND VOLTEX III GRAVITY WARS' + version: int = VersionConstants.SDVX_GRAVITY_WARS - GAME_LIMITED_LOCKED = 1 - GAME_LIMITED_UNLOCKABLE = 2 - GAME_LIMITED_UNLOCKED = 3 + GAME_LIMITED_LOCKED: Final[int] = 1 + GAME_LIMITED_UNLOCKABLE: Final[int] = 2 + GAME_LIMITED_UNLOCKED: Final[int] = 3 - GAME_CURRENCY_PACKETS = 0 - GAME_CURRENCY_BLOCKS = 1 + GAME_CURRENCY_PACKETS: Final[int] = 0 + GAME_CURRENCY_BLOCKS: Final[int] = 1 - GAME_CLEAR_TYPE_NO_CLEAR = 1 - GAME_CLEAR_TYPE_CLEAR = 2 - GAME_CLEAR_TYPE_HARD_CLEAR = 3 - GAME_CLEAR_TYPE_ULTIMATE_CHAIN = 4 - GAME_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN = 5 + GAME_CLEAR_TYPE_NO_CLEAR: Final[int] = 1 + GAME_CLEAR_TYPE_CLEAR: Final[int] = 2 + GAME_CLEAR_TYPE_HARD_CLEAR: Final[int] = 3 + GAME_CLEAR_TYPE_ULTIMATE_CHAIN: Final[int] = 4 + GAME_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: Final[int] = 5 - GAME_GRADE_NO_PLAY = 0 - GAME_GRADE_D = 1 - GAME_GRADE_C = 2 - GAME_GRADE_B = 3 - GAME_GRADE_A = 4 - GAME_GRADE_AA = 5 - GAME_GRADE_AAA = 6 + GAME_GRADE_NO_PLAY: Final[int] = 0 + GAME_GRADE_D: Final[int] = 1 + GAME_GRADE_C: Final[int] = 2 + GAME_GRADE_B: Final[int] = 3 + GAME_GRADE_A: Final[int] = 4 + GAME_GRADE_AA: Final[int] = 5 + GAME_GRADE_AAA: Final[int] = 6 - GAME_CATALOG_TYPE_SONG = 0 - GAME_CATALOG_TYPE_APPEAL_CARD = 1 - GAME_CATALOG_TYPE_CREW = 4 + GAME_CATALOG_TYPE_SONG: Final[int] = 0 + GAME_CATALOG_TYPE_APPEAL_CARD: Final[int] = 1 + GAME_CATALOG_TYPE_CREW: Final[int] = 4 - GAME_GAUGE_TYPE_SKILL = 1 + GAME_GAUGE_TYPE_SKILL: Final[int] = 1 @classmethod def get_settings(cls) -> Dict[str, Any]: diff --git a/bemani/backend/sdvx/gravitywars_s1.py b/bemani/backend/sdvx/gravitywars_s1.py index edee765..6ef9f06 100644 --- a/bemani/backend/sdvx/gravitywars_s1.py +++ b/bemani/backend/sdvx/gravitywars_s1.py @@ -1,5 +1,4 @@ # vim: set fileencoding=utf-8 -import copy from typing import Any, Dict, List from bemani.backend.sdvx.gravitywars import SoundVoltexGravityWars @@ -3263,7 +3262,7 @@ class SoundVoltexGravityWarsSeason1( return game def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() # Update blaster energy and in-game currencies earned_gamecoin_packet = request.child_value('earned_gamecoin_packet') diff --git a/bemani/backend/sdvx/gravitywars_s2.py b/bemani/backend/sdvx/gravitywars_s2.py index ffc27d3..fc88c14 100644 --- a/bemani/backend/sdvx/gravitywars_s2.py +++ b/bemani/backend/sdvx/gravitywars_s2.py @@ -1,5 +1,4 @@ # vim: set fileencoding=utf-8 -import copy from typing import Any, Dict, List, Tuple from bemani.backend.sdvx.gravitywars import SoundVoltexGravityWars @@ -4125,7 +4124,7 @@ class SoundVoltexGravityWarsSeason2( return game def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() # Update blaster energy and in-game currencies earned_gamecoin_packet = request.child_value('earned_gamecoin_packet') diff --git a/bemani/backend/sdvx/heavenlyhaven.py b/bemani/backend/sdvx/heavenlyhaven.py index c6a914d..f9a5d29 100644 --- a/bemani/backend/sdvx/heavenlyhaven.py +++ b/bemani/backend/sdvx/heavenlyhaven.py @@ -1,6 +1,6 @@ # vim: set fileencoding=utf-8 -import copy from typing import Any, Dict, List, Optional, Tuple +from typing_extensions import Final from bemani.backend.ess import EventLogHandler from bemani.backend.sdvx.base import SoundVoltexBase @@ -15,62 +15,62 @@ class SoundVoltexHeavenlyHaven( SoundVoltexBase, ): - name = 'SOUND VOLTEX IV HEAVENLY HAVEN' - version = VersionConstants.SDVX_HEAVENLY_HAVEN + name: str = 'SOUND VOLTEX IV HEAVENLY HAVEN' + version: int = VersionConstants.SDVX_HEAVENLY_HAVEN - GAME_LIMITED_LOCKED = 1 - GAME_LIMITED_UNLOCKABLE = 2 - GAME_LIMITED_UNLOCKED = 3 + GAME_LIMITED_LOCKED: Final[int] = 1 + GAME_LIMITED_UNLOCKABLE: Final[int] = 2 + GAME_LIMITED_UNLOCKED: Final[int] = 3 - GAME_CURRENCY_PACKETS = 0 - GAME_CURRENCY_BLOCKS = 1 + GAME_CURRENCY_PACKETS: Final[int] = 0 + GAME_CURRENCY_BLOCKS: Final[int] = 1 - GAME_CATALOG_TYPE_SONG = 0 - GAME_CATALOG_TYPE_APPEAL_CARD = 1 - GAME_CATALOG_TYPE_CREW = 4 + GAME_CATALOG_TYPE_SONG: Final[int] = 0 + GAME_CATALOG_TYPE_APPEAL_CARD: Final[int] = 1 + GAME_CATALOG_TYPE_CREW: Final[int] = 4 - GAME_CLEAR_TYPE_NO_PLAY = 0 - GAME_CLEAR_TYPE_FAILED = 1 - GAME_CLEAR_TYPE_CLEAR = 2 - GAME_CLEAR_TYPE_HARD_CLEAR = 3 - GAME_CLEAR_TYPE_ULTIMATE_CHAIN = 4 - GAME_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN = 5 + GAME_CLEAR_TYPE_NO_PLAY: Final[int] = 0 + GAME_CLEAR_TYPE_FAILED: Final[int] = 1 + GAME_CLEAR_TYPE_CLEAR: Final[int] = 2 + GAME_CLEAR_TYPE_HARD_CLEAR: Final[int] = 3 + GAME_CLEAR_TYPE_ULTIMATE_CHAIN: Final[int] = 4 + GAME_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: Final[int] = 5 - GAME_GRADE_NO_PLAY = 0 - GAME_GRADE_D = 1 - GAME_GRADE_C = 2 - GAME_GRADE_B = 3 - GAME_GRADE_A = 4 - GAME_GRADE_A_PLUS = 5 - GAME_GRADE_AA = 6 - GAME_GRADE_AA_PLUS = 7 - GAME_GRADE_AAA = 8 - GAME_GRADE_AAA_PLUS = 9 - GAME_GRADE_S = 10 + GAME_GRADE_NO_PLAY: Final[int] = 0 + GAME_GRADE_D: Final[int] = 1 + GAME_GRADE_C: Final[int] = 2 + GAME_GRADE_B: Final[int] = 3 + GAME_GRADE_A: Final[int] = 4 + GAME_GRADE_A_PLUS: Final[int] = 5 + GAME_GRADE_AA: Final[int] = 6 + GAME_GRADE_AA_PLUS: Final[int] = 7 + GAME_GRADE_AAA: Final[int] = 8 + GAME_GRADE_AAA_PLUS: Final[int] = 9 + GAME_GRADE_S: Final[int] = 10 - GAME_SKILL_NAME_ID_LV_01 = 1 - GAME_SKILL_NAME_ID_LV_02 = 2 - GAME_SKILL_NAME_ID_LV_03 = 3 - GAME_SKILL_NAME_ID_LV_04 = 4 - GAME_SKILL_NAME_ID_LV_05 = 5 - GAME_SKILL_NAME_ID_LV_06 = 6 - GAME_SKILL_NAME_ID_LV_07 = 7 - GAME_SKILL_NAME_ID_LV_08 = 8 - GAME_SKILL_NAME_ID_LV_09 = 9 - GAME_SKILL_NAME_ID_LV_10 = 10 - GAME_SKILL_NAME_ID_LV_11 = 11 - GAME_SKILL_NAME_ID_LV_INF = 12 - GAME_SKILL_NAME_ID_KAC_6TH_BODY = 13 - GAME_SKILL_NAME_ID_KAC_6TH_TECHNOLOGY = 14 - GAME_SKILL_NAME_ID_KAC_6TH_HEART = 15 - GAME_SKILL_NAME_ID_TENKAICHI = 16 - GAME_SKILL_NAME_ID_MUSIC_FESTIVAL = 17 - GAME_SKILL_NAME_ID_YELLOWTAIL = 18 # For the April Fool's day courses. - GAME_SKILL_NAME_ID_BMK2017 = 19 - GAME_SKILL_NAME_ID_KAC_7TH_TIGER = 20 - GAME_SKILL_NAME_ID_KAC_7TH_WOLF = 21 - GAME_SKILL_NAME_ID_RIKKA = 22 # For the course that ran from 1/18/2018-2/18/2018 - GAME_SKILL_NAME_ID_KAC_8TH = 23 + GAME_SKILL_NAME_ID_LV_01: Final[int] = 1 + GAME_SKILL_NAME_ID_LV_02: Final[int] = 2 + GAME_SKILL_NAME_ID_LV_03: Final[int] = 3 + GAME_SKILL_NAME_ID_LV_04: Final[int] = 4 + GAME_SKILL_NAME_ID_LV_05: Final[int] = 5 + GAME_SKILL_NAME_ID_LV_06: Final[int] = 6 + GAME_SKILL_NAME_ID_LV_07: Final[int] = 7 + GAME_SKILL_NAME_ID_LV_08: Final[int] = 8 + GAME_SKILL_NAME_ID_LV_09: Final[int] = 9 + GAME_SKILL_NAME_ID_LV_10: Final[int] = 10 + GAME_SKILL_NAME_ID_LV_11: Final[int] = 11 + GAME_SKILL_NAME_ID_LV_INF: Final[int] = 12 + GAME_SKILL_NAME_ID_KAC_6TH_BODY: Final[int] = 13 + GAME_SKILL_NAME_ID_KAC_6TH_TECHNOLOGY: Final[int] = 14 + GAME_SKILL_NAME_ID_KAC_6TH_HEART: Final[int] = 15 + GAME_SKILL_NAME_ID_TENKAICHI: Final[int] = 16 + GAME_SKILL_NAME_ID_MUSIC_FESTIVAL: Final[int] = 17 + GAME_SKILL_NAME_ID_YELLOWTAIL: Final[int] = 18 # For the April Fool's day courses. + GAME_SKILL_NAME_ID_BMK2017: Final[int] = 19 + GAME_SKILL_NAME_ID_KAC_7TH_TIGER: Final[int] = 20 + GAME_SKILL_NAME_ID_KAC_7TH_WOLF: Final[int] = 21 + GAME_SKILL_NAME_ID_RIKKA: Final[int] = 22 # For the course that ran from 1/18/2018-2/18/2018 + GAME_SKILL_NAME_ID_KAC_8TH: Final[int] = 23 # Return the local2 service so that SDVX 4 and above will send certain packets. extra_services: List[str] = [ @@ -4023,7 +4023,7 @@ class SoundVoltexHeavenlyHaven( return game def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() # Update blaster energy and in-game currencies earned_gamecoin_packet = request.child_value('earned_gamecoin_packet') diff --git a/bemani/backend/sdvx/infiniteinfection.py b/bemani/backend/sdvx/infiniteinfection.py index bee7083..953c125 100644 --- a/bemani/backend/sdvx/infiniteinfection.py +++ b/bemani/backend/sdvx/infiniteinfection.py @@ -1,6 +1,6 @@ # vim: set fileencoding=utf-8 -import copy from typing import Any, Dict, List, Optional +from typing_extensions import Final from bemani.backend.ess import EventLogHandler from bemani.backend.sdvx.base import SoundVoltexBase @@ -15,35 +15,35 @@ class SoundVoltexInfiniteInfection( SoundVoltexBase, ): - name = 'SOUND VOLTEX II -infinite infection-' - version = VersionConstants.SDVX_INFINITE_INFECTION + name: str = 'SOUND VOLTEX II -infinite infection-' + version: int = VersionConstants.SDVX_INFINITE_INFECTION - GAME_LIMITED_LOCKED = 1 - GAME_LIMITED_UNLOCKABLE = 2 - GAME_LIMITED_UNLOCKED = 3 + GAME_LIMITED_LOCKED: Final[int] = 1 + GAME_LIMITED_UNLOCKABLE: Final[int] = 2 + GAME_LIMITED_UNLOCKED: Final[int] = 3 - GAME_CURRENCY_PACKETS = 0 - GAME_CURRENCY_BLOCKS = 1 + GAME_CURRENCY_PACKETS: Final[int] = 0 + GAME_CURRENCY_BLOCKS: Final[int] = 1 - GAME_CLEAR_TYPE_NO_CLEAR = 1 - GAME_CLEAR_TYPE_CLEAR = 2 - GAME_CLEAR_TYPE_HARD_CLEAR = 5 - GAME_CLEAR_TYPE_ULTIMATE_CHAIN = 3 - GAME_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN = 4 + GAME_CLEAR_TYPE_NO_CLEAR: Final[int] = 1 + GAME_CLEAR_TYPE_CLEAR: Final[int] = 2 + GAME_CLEAR_TYPE_HARD_CLEAR: Final[int] = 5 + GAME_CLEAR_TYPE_ULTIMATE_CHAIN: Final[int] = 3 + GAME_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: Final[int] = 4 - GAME_GRADE_NO_PLAY = 0 - GAME_GRADE_D = 1 - GAME_GRADE_C = 2 - GAME_GRADE_B = 3 - GAME_GRADE_A = 4 - GAME_GRADE_AA = 5 - GAME_GRADE_AAA = 6 + GAME_GRADE_NO_PLAY: Final[int] = 0 + GAME_GRADE_D: Final[int] = 1 + GAME_GRADE_C: Final[int] = 2 + GAME_GRADE_B: Final[int] = 3 + GAME_GRADE_A: Final[int] = 4 + GAME_GRADE_AA: Final[int] = 5 + GAME_GRADE_AAA: Final[int] = 6 - GAME_CATALOG_TYPE_SONG = 0 - GAME_CATALOG_TYPE_APPEAL_CARD = 1 - GAME_CATALOG_TYPE_SPECIAL_SONG = 2 + GAME_CATALOG_TYPE_SONG: Final[int] = 0 + GAME_CATALOG_TYPE_APPEAL_CARD: Final[int] = 1 + GAME_CATALOG_TYPE_SPECIAL_SONG: Final[int] = 2 - GAME_GAUGE_TYPE_SKILL = 1 + GAME_GAUGE_TYPE_SKILL: Final[int] = 1 @classmethod def get_settings(cls) -> Dict[str, Any]: @@ -2415,7 +2415,7 @@ class SoundVoltexInfiniteInfection( return game def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile: - newprofile = copy.deepcopy(oldprofile) + newprofile = oldprofile.clone() # Update blaster energy and in-game currencies earned_gamecoin_packet = request.child_value('earned_gamecoin_packet') diff --git a/bemani/common/validateddict.py b/bemani/common/validateddict.py index 3a0f131..81bb919 100644 --- a/bemani/common/validateddict.py +++ b/bemani/common/validateddict.py @@ -1,3 +1,4 @@ +import copy from typing import Optional, List, Dict, Any from bemani.common.constants import GameConstants @@ -26,6 +27,9 @@ class ValidatedDict(dict): type. If it does not, the value is not updated. """ + def clone(self) -> "ValidatedDict": + return ValidatedDict(copy.deepcopy(self)) + def get_int(self, name: str, default: int=0) -> int: """ Given the name of a value, return an integer stored under that name. @@ -455,6 +459,9 @@ class Profile(ValidatedDict): self.refid = refid self.extid = extid + def clone(self) -> "Profile": + return Profile(self.game, self.version, self. refid, self.extid, copy.deepcopy(self)) + class PlayStatistics(ValidatedDict): """ @@ -488,3 +495,15 @@ class PlayStatistics(ValidatedDict): self.first_play_timestamp = first_play_timestamp # The timestamp of the very last play session, in seconds. self.last_play_timestamp = last_play_timestamp + + def clone(self) -> "PlayStatistics": + return PlayStatistics( + self.game, + self.total_plays, + self.today_plays, + self.total_days, + self.consecutive_days, + self.first_play_timestamp, + self.last_play_timestamp, + copy.deepcopy(self), + )