From 40dbf1d6b9e578dacc640e7ad3e0cb9e6fcc6dc7 Mon Sep 17 00:00:00 2001 From: Jennifer Taylor Date: Fri, 20 Aug 2021 04:43:59 +0000 Subject: [PATCH] Make Config a real class instead of a Dict, for safer access and defaults. --- bemani/api/app.py | 10 +- bemani/api/types.py | 6 +- bemani/backend/base.py | 20 +- bemani/backend/bishi/factory.py | 6 +- bemani/backend/core/cardmng.py | 2 +- bemani/backend/core/core.py | 32 ++- bemani/backend/core/eacoin.py | 30 +-- bemani/backend/ddr/base.py | 6 +- bemani/backend/ddr/ddrace.py | 4 +- bemani/backend/ddr/factory.py | 6 +- bemani/backend/dispatch.py | 24 +-- bemani/backend/iidx/base.py | 6 +- bemani/backend/iidx/cannonballers.py | 16 +- bemani/backend/iidx/copula.py | 16 +- bemani/backend/iidx/factory.py | 6 +- bemani/backend/iidx/pendual.py | 16 +- bemani/backend/iidx/rootage.py | 14 +- bemani/backend/iidx/sinobuz.py | 16 +- bemani/backend/iidx/spada.py | 16 +- bemani/backend/iidx/tricoro.py | 16 +- bemani/backend/jubeat/factory.py | 6 +- bemani/backend/museca/base.py | 6 +- bemani/backend/museca/factory.py | 6 +- bemani/backend/popn/factory.py | 6 +- bemani/backend/reflec/factory.py | 6 +- bemani/backend/reflec/groovin.py | 2 +- bemani/backend/reflec/volzzabase.py | 2 +- bemani/backend/sdvx/factory.py | 6 +- bemani/data/__init__.py | 2 + bemani/data/config.py | 195 ++++++++++++++++++ bemani/data/data.py | 16 +- bemani/data/mysql/base.py | 5 +- bemani/data/triggers.py | 13 +- bemani/frontend/account/account.py | 4 +- bemani/frontend/admin/admin.py | 12 +- bemani/frontend/app.py | 42 ++-- bemani/frontend/arcade/arcade.py | 2 +- bemani/frontend/bishi/cache.py | 6 +- bemani/frontend/ddr/cache.py | 8 +- bemani/frontend/iidx/cache.py | 8 +- bemani/frontend/jubeat/cache.py | 8 +- bemani/frontend/museca/cache.py | 8 +- bemani/frontend/popn/cache.py | 8 +- bemani/frontend/reflec/cache.py | 8 +- bemani/frontend/sdvx/cache.py | 8 +- bemani/frontend/templates/admin/settings.html | 39 ++-- bemani/frontend/types.py | 6 +- bemani/tests/test_BaseData.py | 7 +- bemani/tests/test_GameData.py | 2 +- bemani/tests/test_IIDXPendual.py | 10 +- bemani/tests/test_NetworkData.py | 4 +- bemani/utils/config.py | 25 +-- bemani/utils/dbutils.py | 18 +- bemani/utils/frontend.py | 18 +- bemani/utils/read.py | 26 +-- bemani/utils/scheduler.py | 24 +-- bemani/utils/services.py | 10 +- config/server.yaml | 45 ++-- 58 files changed, 544 insertions(+), 351 deletions(-) create mode 100644 bemani/data/config.py diff --git a/bemani/api/app.py b/bemani/api/app.py index 1c5f9e0..17b9781 100644 --- a/bemani/api/app.py +++ b/bemani/api/app.py @@ -9,12 +9,12 @@ from bemani.api.exceptions import APIException from bemani.api.objects import RecordsObject, ProfileObject, StatisticsObject, CatalogObject from bemani.api.types import g from bemani.common import GameConstants, APIConstants, VersionConstants -from bemani.data import Data +from bemani.data import Config, Data app = Flask( __name__ ) -config: Dict[str, Any] = {} +config = Config() SUPPORTED_VERSIONS = ['v1'] @@ -172,8 +172,8 @@ def info() -> Dict[str, Any]: return { 'versions': SUPPORTED_VERSIONS, - 'name': g.config.get('name', 'e-AMUSEMENT Network'), - 'email': g.config.get('email', 'nobody@nowhere.com'), + 'name': g.config.name, + 'email': g.config.email, } @@ -209,7 +209,7 @@ def lookup(protoversion: str, requestgame: str, requestversion: str) -> Dict[str ('reflecbeat', GameConstants.REFLEC_BEAT), ('soundvoltex', GameConstants.SDVX), ]: - if constant in g.config['support']: + if constant in g.config.support: gamemapping[gameid] = constant game = gamemapping.get(requestgame) if game is None: diff --git a/bemani/api/types.py b/bemani/api/types.py index 93ff281..9bf4936 100644 --- a/bemani/api/types.py +++ b/bemani/api/types.py @@ -1,13 +1,13 @@ -from typing import Any, Dict, TYPE_CHECKING +from typing import TYPE_CHECKING if TYPE_CHECKING: from flask.ctx import _AppCtxGlobals - from bemani.data import Data + from bemani.data import Config, Data class RequestGlobals(_AppCtxGlobals): - config: Dict[str, Any] + config: Config data: Data authorized: bool diff --git a/bemani/backend/base.py b/bemani/backend/base.py index de67127..c7bb9f6 100644 --- a/bemani/backend/base.py +++ b/bemani/backend/base.py @@ -3,7 +3,7 @@ import traceback from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type from bemani.common import Model, ValidatedDict, Profile, GameConstants, Time -from bemani.data import Data, UserID, RemoteUser +from bemani.data import Config, Data, UserID, RemoteUser class ProfileCreationException(Exception): @@ -41,7 +41,7 @@ class Factory: raise Exception('Override this in subclass!') @classmethod - def run_scheduled_work(cls, data: Data, config: Dict[str, Any]) -> None: + def run_scheduled_work(cls, data: Data, config: Config) -> None: """ Subclasses of this class should use this function to run any scheduled work on classes which it is a factory for. This is usually used for @@ -84,7 +84,7 @@ class Factory: yield (game.game, game.version, game.get_settings()) @classmethod - def create(cls, data: Data, config: Dict[str, Any], model: Model, parentmodel: Optional[Model]=None) -> Optional['Base']: + def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional['Base']: """ Given a modelstring and an optional parent model, return an instantiated game class that can handle a packet. @@ -129,13 +129,13 @@ class Base(ABC): """ name: str - def __init__(self, data: Data, config: Dict[str, Any], model: Model) -> None: + def __init__(self, data: Data, config: Config, model: Model) -> None: self.data = data self.config = config self.model = model @classmethod - def create(cls, data: Data, config: Dict[str, Any], model: Model, parentmodel: Optional[Model]=None) -> Optional['Base']: + def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional['Base']: """ Given a modelstring and an optional parent model, return an instantiated game class that can handle a packet. @@ -179,7 +179,7 @@ class Base(ABC): cls.__registered_handlers.add(handler) @classmethod - def run_scheduled_work(cls, data: Data, config: Dict[str, Any]) -> List[Tuple[str, Dict[str, Any]]]: + def run_scheduled_work(cls, data: Data, config: Config) -> List[Tuple[str, Dict[str, Any]]]: """ Run any out-of-band scheduled work that is applicable to this game. """ @@ -387,23 +387,23 @@ class Base(ABC): self.data.local.game.put_settings(self.game, userid, settings) def get_machine_id(self) -> int: - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) return machine.id def update_machine_name(self, newname: Optional[str]) -> None: if newname is None: return - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) machine.name = newname self.data.local.machine.put_machine(machine) def update_machine_data(self, newdata: Dict[str, Any]) -> None: - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) machine.data.update(newdata) self.data.local.machine.put_machine(machine) def get_game_config(self) -> ValidatedDict: - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: settings = self.data.local.machine.get_settings(machine.arcade, self.game, self.version, 'game_config') else: diff --git a/bemani/backend/bishi/factory.py b/bemani/backend/bishi/factory.py index af17c72..90e094e 100644 --- a/bemani/backend/bishi/factory.py +++ b/bemani/backend/bishi/factory.py @@ -1,9 +1,9 @@ -from typing import Dict, Optional, Any +from typing import Optional from bemani.backend.base import Base, Factory from bemani.backend.bishi.bishi import TheStarBishiBashi from bemani.common import Model -from bemani.data import Data +from bemani.data import Config, Data class BishiBashiFactory(Factory): @@ -18,7 +18,7 @@ class BishiBashiFactory(Factory): Base.register(gamecode, BishiBashiFactory) @classmethod - def create(cls, data: Data, config: Dict[str, Any], model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: + def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: if model.gamecode == 'IBB': return TheStarBishiBashi(data, config, model) diff --git a/bemani/backend/core/cardmng.py b/bemani/backend/core/cardmng.py index 8812db7..a8b1016 100644 --- a/bemani/backend/core/cardmng.py +++ b/bemani/backend/core/cardmng.py @@ -49,7 +49,7 @@ class CardManagerHandler(Base): expired = True refid = self.data.local.user.get_refid(self.game, self.version, userid) - paseli_enabled = self.supports_paseli() and self.config['paseli']['enabled'] + paseli_enabled = self.supports_paseli() and self.config.paseli.enabled root = Node.void('cardmng') root.set_attribute('refid', refid) diff --git a/bemani/backend/core/core.py b/bemani/backend/core/core.py index d447f94..69b27be 100644 --- a/bemani/backend/core/core.py +++ b/bemani/backend/core/core.py @@ -22,7 +22,7 @@ class CoreHandler(Base): node.set_attribute('url', url) return node - url = f'{"https" if self.config["server"]["https"] else "http"}://{self.config["server"]["address"]}:{self.config["server"]["port"]}/' + url = f'{"https" if self.config.server.https else "http"}://{self.config.server.address}:{self.config.server.port}/' root = Node.void('services') root.set_attribute('expire', '600') # This can be set to 'operation', 'debug', 'test', and 'factory'. @@ -45,13 +45,9 @@ class CoreHandler(Base): root.add_child(item(srv, url)) root.add_child(item('ntp', 'ntp://pool.ntp.org/')) - # Look up keepalive override if exists, otherwise use the server address - if 'keepalive' in self.config['server']: - keepalive = self.config['server']['keepalive'] - else: - keepalive = self.config['server']['address'] - # Translate to a raw IP because we can't give out a host here - keepalive = socket.gethostbyname(keepalive) + + # Translate keepalive to a raw IP because we can't give out a host here + keepalive = socket.gethostbyname(self.config.server.keepalive) root.add_child(item( 'keepalive', f'http://{keepalive}/core/keepalive?pa={keepalive}&ia={keepalive}&ga={keepalive}&ma={keepalive}&t1=2&t2=10', @@ -65,7 +61,7 @@ class CoreHandler(Base): """ # Reports that a machine is booting. Overloaded to enable/disable paseli root = Node.void('pcbtracker') - root.set_attribute('ecenable', '1' if (self.supports_paseli() and self.config['paseli']['enabled']) else '0') + root.set_attribute('ecenable', '1' if (self.supports_paseli() and self.config.paseli.enabled) else '0') root.set_attribute('expire', '600') return root @@ -84,8 +80,8 @@ class CoreHandler(Base): 'name': name, 'value': value, 'model': str(self.model), - 'pcbid': self.config['machine']['pcbid'], - 'ip': self.config['client']['address'], + 'pcbid': self.config.machine.pcbid, + 'ip': self.config.client.address, }, timestamp=timestamp, ) @@ -124,7 +120,7 @@ class CoreHandler(Base): which expects to return a bunch of information about the arcade this cabinet is in, as well as some settings for URLs and the name of the cab. """ - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) root = Node.void('facility') root.set_attribute('expire', '600') @@ -140,7 +136,7 @@ class CoreHandler(Base): line.add_child(Node.u8('class', 0)) portfw = Node.void('portfw') - portfw.add_child(Node.ipv4('globalip', self.config['client']['address'])) + portfw.add_child(Node.ipv4('globalip', self.config.client.address)) portfw.add_child(Node.u16('globalport', machine.port)) portfw.add_child(Node.u16('privateport', machine.port)) @@ -160,11 +156,11 @@ class CoreHandler(Base): eapass.add_child(Node.u16('valid', 365)) url = Node.void('url') - url.add_child(Node.string('eapass', self.config['server']['uri'] or 'www.ea-pass.konami.net')) - url.add_child(Node.string('arcadefan', self.config['server']['uri'] or 'www.konami.jp/am')) - url.add_child(Node.string('konaminetdx', self.config['server']['uri'] or 'http://am.573.jp')) - url.add_child(Node.string('konamiid', self.config['server']['uri'] or 'https://id.konami.net')) - url.add_child(Node.string('eagate', self.config['server']['uri'] or 'http://eagate.573.jp')) + url.add_child(Node.string('eapass', self.config.server.uri or 'www.ea-pass.konami.net')) + url.add_child(Node.string('arcadefan', self.config.server.uri or 'www.konami.jp/am')) + url.add_child(Node.string('konaminetdx', self.config.server.uri or 'http://am.573.jp')) + url.add_child(Node.string('konamiid', self.config.server.uri or 'https://id.konami.net')) + url.add_child(Node.string('eagate', self.config.server.uri or 'http://eagate.573.jp')) share.add_child(eacoin) share.add_child(url) diff --git a/bemani/backend/core/eacoin.py b/bemani/backend/core/eacoin.py index e4b76c5..a1d5d18 100644 --- a/bemani/backend/core/eacoin.py +++ b/bemani/backend/core/eacoin.py @@ -27,7 +27,7 @@ class PASELIHandler(Base): """ method = request.attribute('method') - if not self.config['paseli']['enabled']: + if not self.config.paseli.enabled: # Refuse to respond, we don't have PASELI enabled print("PASELI not enabled, ignoring eacoin request") root = Node.void('eacoin') @@ -61,15 +61,15 @@ class PASELIHandler(Base): session = self.data.local.user.create_session(userid) - if self.config['paseli']['infinite']: + if self.config.paseli.infinite: balance = PASELIHandler.INFINITE_PASELI_AMOUNT else: - if self.config['machine']['arcade'] is None: + 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']) + 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)) @@ -89,13 +89,13 @@ class PASELIHandler(Base): root.set_attribute('status', str(Status.NO_PROFILE)) return root - if self.config['machine']['arcade'] is None: + 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']) + 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") @@ -136,10 +136,10 @@ class PASELIHandler(Base): print("Invalid session for eacoin consume request") return make_resp(2, 0) - if self.config['paseli']['infinite']: + if self.config.paseli.infinite: balance = PASELIHandler.INFINITE_PASELI_AMOUNT - payment else: - if self.config['machine']['arcade'] is None: + 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. @@ -147,11 +147,11 @@ class PASELIHandler(Base): 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) + 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'])) + return make_resp(1, self.data.local.user.get_balance(userid, self.config.machine.arcade)) else: self.data.local.network.put_event( 'paseli_transaction', @@ -160,10 +160,10 @@ class PASELIHandler(Base): 'balance': balance, 'service': -service, 'reason': details, - 'pcbid': self.config['machine']['pcbid'], + 'pcbid': self.config.machine.pcbid, }, userid=userid, - arcadeid=self.config['machine']['arcade'], + arcadeid=self.config.machine.arcade, ) return make_resp(0, balance) @@ -191,7 +191,7 @@ class PASELIHandler(Base): # 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']) + 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 @@ -394,13 +394,13 @@ class PASELIHandler(Base): root.set_attribute('status', str(Status.NO_PROFILE)) return root - if self.config['machine']['arcade'] is None: + 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']) + 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") diff --git a/bemani/backend/ddr/base.py b/bemani/backend/ddr/base.py index 477b691..1ce9b39 100644 --- a/bemani/backend/ddr/base.py +++ b/bemani/backend/ddr/base.py @@ -1,10 +1,10 @@ # vim: set fileencoding=utf-8 -from typing import Optional, Dict, List, Any +from typing import Optional, List from bemani.backend.base import Base from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler from bemani.common import Model, Profile, ValidatedDict, GameConstants, DBConstants, Time -from bemani.data import Data, Score, UserID, ScoreSaveException +from bemani.data import Config, Data, Score, UserID, ScoreSaveException from bemani.protocol import Node @@ -51,7 +51,7 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): CHART_DOUBLE_EXPERT = 8 CHART_DOUBLE_CHALLENGE = 9 - def __init__(self, data: Data, config: Dict[str, Any], model: Model) -> None: + def __init__(self, data: Data, config: Config, model: Model) -> None: super().__init__(data, config, model) if model.rev == 'X': self.omnimix = True diff --git a/bemani/backend/ddr/ddrace.py b/bemani/backend/ddr/ddrace.py index 9285ca5..b5c3ebb 100644 --- a/bemani/backend/ddr/ddrace.py +++ b/bemani/backend/ddr/ddrace.py @@ -466,7 +466,7 @@ class DDRAce( response.add_child(data) data.add_child(Node.s32('recordtype', requestdata.child_value('loadflag'))) - thismachine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + thismachine = self.data.local.machine.get_machine(self.config.machine.pcbid) machines_by_id: Dict[int, Optional[Machine]] = {thismachine.id: thismachine} loadkind = requestdata.child_value('loadflag') @@ -559,7 +559,7 @@ class DDRAce( if userid is None: raise Exception('Expecting valid UserID to create new profile!') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) profile = Profile( self.game, self.version, diff --git a/bemani/backend/ddr/factory.py b/bemani/backend/ddr/factory.py index 685f57b..e44b812 100644 --- a/bemani/backend/ddr/factory.py +++ b/bemani/backend/ddr/factory.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional, Any +from typing import Optional from bemani.backend.base import Base, Factory from bemani.backend.ddr.stubs import ( @@ -21,7 +21,7 @@ from bemani.backend.ddr.ddr2014 import DDR2014 from bemani.backend.ddr.ddrace import DDRAce from bemani.backend.ddr.ddra20 import DDRA20 from bemani.common import Model, VersionConstants -from bemani.data import Data +from bemani.data import Config, Data class DDRFactory(Factory): @@ -52,7 +52,7 @@ class DDRFactory(Factory): Base.register(gamecode, DDRFactory) @classmethod - def create(cls, data: Data, config: Dict[str, Any], model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: + def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: def version_from_date(date: int) -> Optional[int]: if date < 2014051200: diff --git a/bemani/backend/dispatch.py b/bemani/backend/dispatch.py index 699e977..732f8a0 100644 --- a/bemani/backend/dispatch.py +++ b/bemani/backend/dispatch.py @@ -1,9 +1,8 @@ -import copy -from typing import Optional, Dict, Any +from typing import Optional, Any from bemani.backend.base import Model, Base, Status from bemani.protocol import Node -from bemani.data import Data +from bemani.data import Config, Data class UnrecognizedPCBIDException(Exception): @@ -20,7 +19,7 @@ class Dispatch: class and then returning a response. """ - def __init__(self, config: Dict[str, Any], data: Data, verbose: bool) -> None: + def __init__(self, config: Config, data: Data, verbose: bool) -> None: """ Initialize the Dispatch object. @@ -77,9 +76,9 @@ class Dispatch: # If we are enforcing, bail out if we don't recognize thie ID pcb = self.__data.local.machine.get_machine(pcbid) - if self.__config['server']['enforce_pcbid'] and pcb is None: + if self.__config.server.enforce_pcbid and pcb is None: self.log("Unrecognized PCBID {}", pcbid) - raise UnrecognizedPCBIDException(pcbid, modelstring, self.__config['client']['address']) + raise UnrecognizedPCBIDException(pcbid, modelstring, self.__config.client.address) # If we don't have a Machine, but we aren't enforcing, we must create it if pcb is None: @@ -87,7 +86,7 @@ class Dispatch: request = tree.children[0] - config = copy.copy(self.__config) + config = self.__config.clone() config['machine'] = { 'pcbid': pcbid, 'arcade': pcb.arcade, @@ -103,9 +102,6 @@ class Dispatch: if arcade.data.get_bool('mask_services_url'): # Mask the address, no matter what the server settings are config['server']['uri'] = None - # If we don't have a server URI, we should add the default - if 'uri' not in config['server']: - config['server']['uri'] = None game = Base.create(self.__data, config, model) method = request.attribute('method') @@ -113,10 +109,10 @@ class Dispatch: # If we are enforcing, make sure the PCBID isn't specified to be # game-specific - if self.__config['server']['enforce_pcbid'] and pcb.game is not None: + if config.server.enforce_pcbid and pcb.game is not None: if pcb.game != game.game: self.log("PCBID {} assigned to game {}, but connected from game {}", pcbid, pcb.game, game.game) - raise UnrecognizedPCBIDException(pcbid, modelstring, self.__config['client']['address']) + raise UnrecognizedPCBIDException(pcbid, modelstring, config.client.address) if pcb.version is not None: if pcb.version > 0 and pcb.version != game.version: self.log( @@ -127,7 +123,7 @@ class Dispatch: game.game, game.version, ) - raise UnrecognizedPCBIDException(pcbid, modelstring, self.__config['client']['address']) + raise UnrecognizedPCBIDException(pcbid, modelstring, config.client.address) if pcb.version < 0 and (-pcb.version) < game.version: self.log( "PCBID {} assigned to game {} maximum version {}, but connected from game {} version {}", @@ -137,7 +133,7 @@ class Dispatch: game.game, game.version, ) - raise UnrecognizedPCBIDException(pcbid, modelstring, self.__config['client']['address']) + raise UnrecognizedPCBIDException(pcbid, modelstring, config.client.address) # First, try to handle with specific service/method function try: diff --git a/bemani/backend/iidx/base.py b/bemani/backend/iidx/base.py index c8fde56..ebc83a5 100644 --- a/bemani/backend/iidx/base.py +++ b/bemani/backend/iidx/base.py @@ -5,7 +5,7 @@ from typing import Optional, Dict, Any, List, Tuple from bemani.backend.base import Base from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler from bemani.common import Profile, ValidatedDict, Model, GameConstants, DBConstants, Parallel -from bemani.data import Data, Score, Machine, UserID +from bemani.data import Config, Data, Score, Machine, UserID from bemani.protocol import Node @@ -75,7 +75,7 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): GHOST_TYPE_RIVAL_TOP = 800 GHOST_TYPE_RIVAL_AVERAGE = 900 - def __init__(self, data: Data, config: Dict[str, Any], model: Model) -> None: + def __init__(self, data: Data, config: Config, model: Model) -> None: super().__init__(data, config, model) if model.rev == 'X': self.omnimix = True @@ -189,7 +189,7 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): return None def machine_joined_arcade(self) -> bool: - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) return machine.arcade is not None def get_clear_rates( diff --git a/bemani/backend/iidx/cannonballers.py b/bemani/backend/iidx/cannonballers.py index 3b7ef3c..f31f2ba 100644 --- a/bemani/backend/iidx/cannonballers.py +++ b/bemani/backend/iidx/cannonballers.py @@ -288,7 +288,7 @@ class IIDXCannonBallers(IIDXCourse, IIDXBase): raise Exception('Invalid cltype!') def handle_IIDX25shop_getname_request(self, request: Node) -> Node: - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine is not None: machine_name = machine.name close = machine.data.get_bool('close') @@ -333,7 +333,7 @@ class IIDXCannonBallers(IIDXCourse, IIDXBase): def handle_IIDX25shop_getconvention_request(self, request: Node) -> Node: root = Node.void('IIDX25shop') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -350,7 +350,7 @@ class IIDXCannonBallers(IIDXCourse, IIDXBase): return root def handle_IIDX25shop_setconvention_request(self, request: Node) -> Node: - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = ValidatedDict() course.replace_int('music_0', request.child_value('music_0')) @@ -376,7 +376,7 @@ class IIDXCannonBallers(IIDXCourse, IIDXBase): # Chart type 6 is presumably beginner mode, but it crashes the game return root - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -677,7 +677,7 @@ class IIDXCannonBallers(IIDXCourse, IIDXBase): if self.machine_joined_arcade(): game_config = self.get_game_config() global_scores = game_config.get_bool('global_shop_ranking') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) else: # If we aren't in an arcade, we can only show global scores global_scores = True @@ -1425,7 +1425,7 @@ class IIDXCannonBallers(IIDXCourse, IIDXBase): # Look up judge window adjustments judge_dict = profile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) # Profile data pcdata = Node.void('pcdata') @@ -1921,10 +1921,10 @@ class IIDXCannonBallers(IIDXCourse, IIDXBase): newprofile.replace_int('rtype', int(request.attribute('rtype'))) # Update judge window adjustments per-machine judge_dict = newprofile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) machine_judge.replace_int('single', int(request.attribute('s_judgeAdj'))) machine_judge.replace_int('double', int(request.attribute('d_judgeAdj'))) - judge_dict.replace_dict(self.config['machine']['pcbid'], machine_judge) + judge_dict.replace_dict(self.config.machine.pcbid, machine_judge) newprofile.replace_dict('machine_judge_adjust', judge_dict) # Secret flags saving diff --git a/bemani/backend/iidx/copula.py b/bemani/backend/iidx/copula.py index 4ad56d2..3dfa3b8 100644 --- a/bemani/backend/iidx/copula.py +++ b/bemani/backend/iidx/copula.py @@ -293,7 +293,7 @@ class IIDXCopula(IIDXCourse, IIDXBase): if method == 'getname': root = Node.void('IIDX23shop') root.set_attribute('cls_opt', '0') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) root.set_attribute('opname', machine.name) root.set_attribute('pid', '51') return root @@ -309,7 +309,7 @@ class IIDXCopula(IIDXCourse, IIDXBase): if method == 'getconvention': root = Node.void('IIDX23shop') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -327,7 +327,7 @@ class IIDXCopula(IIDXCourse, IIDXBase): if method == 'setconvention': root = Node.void('IIDX23shop') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = ValidatedDict() course.replace_int('music_0', request.child_value('music_0')) @@ -364,7 +364,7 @@ class IIDXCopula(IIDXCourse, IIDXBase): # Chart type 6 is presumably beginner mode, but it crashes the game return root - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -571,7 +571,7 @@ class IIDXCopula(IIDXCourse, IIDXBase): if self.machine_joined_arcade(): game_config = self.get_game_config() global_scores = game_config.get_bool('global_shop_ranking') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) else: # If we aren't in an arcade, we can only show global scores global_scores = True @@ -1477,7 +1477,7 @@ class IIDXCopula(IIDXCourse, IIDXBase): # Look up judge window adjustments judge_dict = profile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) # Profile data pcdata = Node.void('pcdata') @@ -1925,10 +1925,10 @@ class IIDXCopula(IIDXCourse, IIDXBase): # Update judge window adjustments per-machine judge_dict = newprofile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) machine_judge.replace_int('single', int(request.attribute('s_judgeAdj'))) machine_judge.replace_int('double', int(request.attribute('d_judgeAdj'))) - judge_dict.replace_dict(self.config['machine']['pcbid'], machine_judge) + judge_dict.replace_dict(self.config.machine.pcbid, machine_judge) newprofile.replace_dict('machine_judge_adjust', judge_dict) # Secret flags saving diff --git a/bemani/backend/iidx/factory.py b/bemani/backend/iidx/factory.py index c12e911..44536ac 100644 --- a/bemani/backend/iidx/factory.py +++ b/bemani/backend/iidx/factory.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional, Any +from typing import Optional from bemani.backend.base import Base, Factory from bemani.backend.iidx.stubs import ( @@ -32,7 +32,7 @@ from bemani.backend.iidx.rootage import IIDXRootage from bemani.backend.iidx.heroicverse import IIDXHeroicVerse from bemani.backend.iidx.bistrover import IIDXBistrover from bemani.common import Model, VersionConstants -from bemani.data import Data +from bemani.data import Config, Data class IIDXFactory(Factory): @@ -74,7 +74,7 @@ class IIDXFactory(Factory): Base.register(gamecode, IIDXFactory) @classmethod - def create(cls, data: Data, config: Dict[str, Any], model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: + def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: def version_from_date(date: int) -> Optional[int]: if date < 2013100200: diff --git a/bemani/backend/iidx/pendual.py b/bemani/backend/iidx/pendual.py index 90fe1e1..1f4172f 100644 --- a/bemani/backend/iidx/pendual.py +++ b/bemani/backend/iidx/pendual.py @@ -161,7 +161,7 @@ class IIDXPendual(IIDXCourse, IIDXBase): if method == 'getname': root = Node.void('IIDX22shop') root.set_attribute('cls_opt', '0') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) root.set_attribute('opname', machine.name) root.set_attribute('pid', '51') return root @@ -177,7 +177,7 @@ class IIDXPendual(IIDXCourse, IIDXBase): if method == 'getconvention': root = Node.void('IIDX22shop') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -195,7 +195,7 @@ class IIDXPendual(IIDXCourse, IIDXBase): if method == 'setconvention': root = Node.void('IIDX22shop') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = ValidatedDict() course.replace_int('music_0', request.child_value('music_0')) @@ -227,7 +227,7 @@ class IIDXPendual(IIDXCourse, IIDXBase): # Chart type 6 is presumably beginner mode, but it crashes the game return root - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -555,7 +555,7 @@ class IIDXPendual(IIDXCourse, IIDXBase): if self.machine_joined_arcade(): game_config = self.get_game_config() global_scores = game_config.get_bool('global_shop_ranking') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) else: # If we aren't in an arcade, we can only show global scores global_scores = True @@ -1390,7 +1390,7 @@ class IIDXPendual(IIDXCourse, IIDXBase): # Look up judge window adjustments judge_dict = profile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) # Profile data pcdata = Node.void('pcdata') @@ -1771,10 +1771,10 @@ class IIDXPendual(IIDXCourse, IIDXBase): # Update judge window adjustments per-machine judge_dict = newprofile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) machine_judge.replace_int('single', int(request.attribute('s_judgeAdj'))) machine_judge.replace_int('double', int(request.attribute('d_judgeAdj'))) - judge_dict.replace_dict(self.config['machine']['pcbid'], machine_judge) + judge_dict.replace_dict(self.config.machine.pcbid, machine_judge) newprofile.replace_dict('machine_judge_adjust', judge_dict) # Secret flags saving diff --git a/bemani/backend/iidx/rootage.py b/bemani/backend/iidx/rootage.py index 80cad5b..21f9ee1 100644 --- a/bemani/backend/iidx/rootage.py +++ b/bemani/backend/iidx/rootage.py @@ -289,7 +289,7 @@ class IIDXRootage(IIDXCourse, IIDXBase): raise Exception('Invalid cltype!') def handle_IIDX26shop_getname_request(self, request: Node) -> Node: - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine is not None: machine_name = machine.name close = machine.data.get_bool('close') @@ -334,7 +334,7 @@ class IIDXRootage(IIDXCourse, IIDXBase): def handle_IIDX26shop_getconvention_request(self, request: Node) -> Node: root = Node.void('IIDX26shop') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -351,7 +351,7 @@ class IIDXRootage(IIDXCourse, IIDXBase): return root def handle_IIDX26shop_setconvention_request(self, request: Node) -> Node: - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = ValidatedDict() course.replace_int('music_0', request.child_value('music_0')) @@ -530,7 +530,7 @@ class IIDXRootage(IIDXCourse, IIDXBase): if self.machine_joined_arcade(): game_config = self.get_game_config() global_scores = game_config.get_bool('global_shop_ranking') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) else: # If we aren't in an arcade, we can only show global scores global_scores = True @@ -1100,7 +1100,7 @@ class IIDXRootage(IIDXCourse, IIDXBase): # Look up judge window adjustments judge_dict = profile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) # Profile data pcdata = Node.void('pcdata') @@ -1638,10 +1638,10 @@ class IIDXRootage(IIDXCourse, IIDXBase): # Update judge window adjustments per-machine judge_dict = newprofile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) machine_judge.replace_int('single', int(request.attribute('s_judgeAdj'))) machine_judge.replace_int('double', int(request.attribute('d_judgeAdj'))) - judge_dict.replace_dict(self.config['machine']['pcbid'], machine_judge) + judge_dict.replace_dict(self.config.machine.pcbid, machine_judge) newprofile.replace_dict('machine_judge_adjust', judge_dict) # Secret flags saving diff --git a/bemani/backend/iidx/sinobuz.py b/bemani/backend/iidx/sinobuz.py index 2b99228..81f6972 100644 --- a/bemani/backend/iidx/sinobuz.py +++ b/bemani/backend/iidx/sinobuz.py @@ -288,7 +288,7 @@ class IIDXSinobuz(IIDXCourse, IIDXBase): raise Exception('Invalid cltype!') def handle_IIDX24shop_getname_request(self, request: Node) -> Node: - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine is not None: machine_name = machine.name close = machine.data.get_bool('close') @@ -333,7 +333,7 @@ class IIDXSinobuz(IIDXCourse, IIDXBase): def handle_IIDX24shop_getconvention_request(self, request: Node) -> Node: root = Node.void('IIDX24shop') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -350,7 +350,7 @@ class IIDXSinobuz(IIDXCourse, IIDXBase): return root def handle_IIDX24shop_setconvention_request(self, request: Node) -> Node: - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = ValidatedDict() course.replace_int('music_0', request.child_value('music_0')) @@ -376,7 +376,7 @@ class IIDXSinobuz(IIDXCourse, IIDXBase): # Chart type 6 is presumably beginner mode, but it crashes the game return root - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -675,7 +675,7 @@ class IIDXSinobuz(IIDXCourse, IIDXBase): if self.machine_joined_arcade(): game_config = self.get_game_config() global_scores = game_config.get_bool('global_shop_ranking') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) else: # If we aren't in an arcade, we can only show global scores global_scores = True @@ -1449,7 +1449,7 @@ class IIDXSinobuz(IIDXCourse, IIDXBase): # Look up judge window adjustments judge_dict = profile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) # Profile data pcdata = Node.void('pcdata') @@ -1936,10 +1936,10 @@ class IIDXSinobuz(IIDXCourse, IIDXBase): # Update judge window adjustments per-machine judge_dict = newprofile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) machine_judge.replace_int('single', int(request.attribute('s_judgeAdj'))) machine_judge.replace_int('double', int(request.attribute('d_judgeAdj'))) - judge_dict.replace_dict(self.config['machine']['pcbid'], machine_judge) + judge_dict.replace_dict(self.config.machine.pcbid, machine_judge) newprofile.replace_dict('machine_judge_adjust', judge_dict) # Secret flags saving diff --git a/bemani/backend/iidx/spada.py b/bemani/backend/iidx/spada.py index f67700b..e68676f 100644 --- a/bemani/backend/iidx/spada.py +++ b/bemani/backend/iidx/spada.py @@ -266,7 +266,7 @@ class IIDXSpada(IIDXBase): if method == 'getname': root = Node.void('IIDX21shop') root.set_attribute('cls_opt', '0') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) root.set_attribute('opname', machine.name) root.set_attribute('pid', '51') return root @@ -282,7 +282,7 @@ class IIDXSpada(IIDXBase): if method == 'getconvention': root = Node.void('IIDX21shop') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -300,7 +300,7 @@ class IIDXSpada(IIDXBase): if method == 'setconvention': root = Node.void('IIDX21shop') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = ValidatedDict() course.replace_int('music_0', request.child_value('music_0')) @@ -332,7 +332,7 @@ class IIDXSpada(IIDXBase): # Chart type 6 is presumably beginner mode, but it crashes the game return root - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -503,7 +503,7 @@ class IIDXSpada(IIDXBase): if self.machine_joined_arcade(): game_config = self.get_game_config() global_scores = game_config.get_bool('global_shop_ranking') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) else: # If we aren't in an arcade, we can only show global scores global_scores = True @@ -1089,7 +1089,7 @@ class IIDXSpada(IIDXBase): # Look up judge window adjustments judge_dict = profile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) # Profile data pcdata = Node.void('pcdata') @@ -1516,10 +1516,10 @@ class IIDXSpada(IIDXBase): # Update judge window adjustments per-machine judge_dict = newprofile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) machine_judge.replace_int('single', int(request.attribute('s_judgeAdj'))) machine_judge.replace_int('double', int(request.attribute('d_judgeAdj'))) - judge_dict.replace_dict(self.config['machine']['pcbid'], machine_judge) + judge_dict.replace_dict(self.config.machine.pcbid, machine_judge) newprofile.replace_dict('machine_judge_adjust', judge_dict) # Secret flags saving diff --git a/bemani/backend/iidx/tricoro.py b/bemani/backend/iidx/tricoro.py index e60853b..d9c3c8f 100644 --- a/bemani/backend/iidx/tricoro.py +++ b/bemani/backend/iidx/tricoro.py @@ -265,7 +265,7 @@ class IIDXTricoro(IIDXBase): if method == 'getname': root = Node.void('shop') root.set_attribute('cls_opt', '0') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) root.set_attribute('opname', machine.name) root.set_attribute('pid', '51') return root @@ -281,7 +281,7 @@ class IIDXTricoro(IIDXBase): if method == 'getconvention': root = Node.void('shop') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -299,7 +299,7 @@ class IIDXTricoro(IIDXBase): if method == 'setconvention': root = Node.void('shop') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = ValidatedDict() course.replace_int('music_0', request.child_value('music_0')) @@ -331,7 +331,7 @@ class IIDXTricoro(IIDXBase): # Chart type 6 is presumably beginner mode, but it crashes the game return root - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: course = self.data.local.machine.get_settings(machine.arcade, self.game, self.music_version, 'shop_course') else: @@ -503,7 +503,7 @@ class IIDXTricoro(IIDXBase): if self.machine_joined_arcade(): game_config = self.get_game_config() global_scores = game_config.get_bool('global_shop_ranking') - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) else: # If we aren't in an arcade, we can only show global scores global_scores = True @@ -985,7 +985,7 @@ class IIDXTricoro(IIDXBase): # Look up judge window adjustments judge_dict = profile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) # Profile data pcdata = Node.void('pcdata') @@ -1292,9 +1292,9 @@ class IIDXTricoro(IIDXBase): # Update judge window adjustments per-machine judge_dict = newprofile.get_dict('machine_judge_adjust') - machine_judge = judge_dict.get_dict(self.config['machine']['pcbid']) + machine_judge = judge_dict.get_dict(self.config.machine.pcbid) machine_judge.replace_int('adj', int(request.attribute('judgeAdj'))) - judge_dict.replace_dict(self.config['machine']['pcbid'], machine_judge) + judge_dict.replace_dict(self.config.machine.pcbid, machine_judge) newprofile.replace_dict('machine_judge_adjust', judge_dict) # Secret flags saving diff --git a/bemani/backend/jubeat/factory.py b/bemani/backend/jubeat/factory.py index 27b8c41..31030cb 100644 --- a/bemani/backend/jubeat/factory.py +++ b/bemani/backend/jubeat/factory.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional, Any +from typing import Optional from bemani.backend.base import Base, Factory from bemani.backend.jubeat.stubs import ( @@ -17,7 +17,7 @@ from bemani.backend.jubeat.qubell import JubeatQubell from bemani.backend.jubeat.clan import JubeatClan from bemani.backend.jubeat.festo import JubeatFesto from bemani.common import Model -from bemani.data import Data +from bemani.data import Config, Data class JubeatFactory(Factory): @@ -44,7 +44,7 @@ class JubeatFactory(Factory): Base.register(gamecode, JubeatFactory) @classmethod - def create(cls, data: Data, config: Dict[str, Any], model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: + def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: if model.gamecode == 'H44': return Jubeat(data, config, model) if model.gamecode == 'I44': diff --git a/bemani/backend/museca/base.py b/bemani/backend/museca/base.py index e1c0daf..425bbd6 100644 --- a/bemani/backend/museca/base.py +++ b/bemani/backend/museca/base.py @@ -1,10 +1,10 @@ # vim: set fileencoding=utf-8 -from typing import Dict, Optional, Any +from typing import Dict, Optional from bemani.backend.base import Base from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler from bemani.common import Profile, ValidatedDict, GameConstants, DBConstants, Parallel, Model -from bemani.data import UserID, Data +from bemani.data import UserID, Config, Data from bemani.protocol import Node @@ -33,7 +33,7 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base): CLEAR_TYPE_CLEARED = DBConstants.MUSECA_CLEAR_TYPE_CLEARED CLEAR_TYPE_FULL_COMBO = DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO - def __init__(self, data: Data, config: Dict[str, Any], model: Model) -> None: + def __init__(self, data: Data, config: Config, model: Model) -> None: super().__init__(data, config, model) if model.rev == 'X': self.omnimix = True diff --git a/bemani/backend/museca/factory.py b/bemani/backend/museca/factory.py index b6e521b..4149565 100644 --- a/bemani/backend/museca/factory.py +++ b/bemani/backend/museca/factory.py @@ -1,10 +1,10 @@ -from typing import Any, Dict, List, Optional, Type +from typing import List, Optional, Type from bemani.backend.base import Base, Factory from bemani.backend.museca.museca1 import Museca1 from bemani.backend.museca.museca1plus import Museca1Plus from bemani.common import Model, VersionConstants -from bemani.data import Data +from bemani.data import Config, Data class MusecaFactory(Factory): @@ -20,7 +20,7 @@ class MusecaFactory(Factory): Base.register(gamecode, MusecaFactory) @classmethod - def create(cls, data: Data, config: Dict[str, Any], model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: + def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: def version_from_date(date: int) -> Optional[int]: if date <= 2016072600: diff --git a/bemani/backend/popn/factory.py b/bemani/backend/popn/factory.py index 1342be7..166c3ad 100644 --- a/bemani/backend/popn/factory.py +++ b/bemani/backend/popn/factory.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional, Any +from typing import Optional from bemani.backend.base import Base, Factory from bemani.backend.popn.stubs import ( @@ -29,7 +29,7 @@ from bemani.backend.popn.eclale import PopnMusicEclale from bemani.backend.popn.usaneko import PopnMusicUsaNeko from bemani.backend.popn.peace import PopnMusicPeace from bemani.common import Model, VersionConstants -from bemani.data import Data +from bemani.data import Config, Data class PopnMusicFactory(Factory): @@ -68,7 +68,7 @@ class PopnMusicFactory(Factory): Base.register(gamecode, PopnMusicFactory) @classmethod - def create(cls, data: Data, config: Dict[str, Any], model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: + def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: def version_from_date(date: int) -> Optional[int]: if date <= 2014061900: diff --git a/bemani/backend/reflec/factory.py b/bemani/backend/reflec/factory.py index 8970ebb..33de431 100644 --- a/bemani/backend/reflec/factory.py +++ b/bemani/backend/reflec/factory.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, Type +from typing import List, Optional, Type from bemani.backend.base import Base, Factory from bemani.backend.reflec.reflecbeat import ReflecBeat @@ -8,7 +8,7 @@ from bemani.backend.reflec.groovin import ReflecBeatGroovin from bemani.backend.reflec.volzza import ReflecBeatVolzza from bemani.backend.reflec.volzza2 import ReflecBeatVolzza2 from bemani.common import Model, VersionConstants -from bemani.data import Data +from bemani.data import Config, Data class ReflecBeatFactory(Factory): @@ -28,7 +28,7 @@ class ReflecBeatFactory(Factory): Base.register(gamecode, ReflecBeatFactory) @classmethod - def create(cls, data: Data, config: Dict[str, Any], model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: + def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: def version_from_date(date: int) -> Optional[int]: if date < 2014060400: diff --git a/bemani/backend/reflec/groovin.py b/bemani/backend/reflec/groovin.py index a98dad6..275ac60 100644 --- a/bemani/backend/reflec/groovin.py +++ b/bemani/backend/reflec/groovin.py @@ -295,7 +295,7 @@ class ReflecBeatGroovin(ReflecBeatBase): all_profiles = self.data.local.user.get_all_profiles(self.game, self.version) all_attempts = self.data.local.music.get_all_attempts(self.game, self.version, timelimit=(Time.beginning_of_today() - Time.SECONDS_IN_DAY)) - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: lids = [ machine.id for machine in self.data.local.machine.get_all_machines(machine.arcade) diff --git a/bemani/backend/reflec/volzzabase.py b/bemani/backend/reflec/volzzabase.py index fc7c46b..daba2f7 100644 --- a/bemani/backend/reflec/volzzabase.py +++ b/bemani/backend/reflec/volzzabase.py @@ -81,7 +81,7 @@ class ReflecBeatVolzzaBase(ReflecBeatBase): all_profiles = self.data.local.user.get_all_profiles(self.game, self.version) all_attempts = self.data.local.music.get_all_attempts(self.game, self.version, timelimit=(Time.beginning_of_today() - Time.SECONDS_IN_DAY)) - machine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) + machine = self.data.local.machine.get_machine(self.config.machine.pcbid) if machine.arcade is not None: lids = [ machine.id for machine in self.data.local.machine.get_all_machines(machine.arcade) diff --git a/bemani/backend/sdvx/factory.py b/bemani/backend/sdvx/factory.py index 1f10d29..ea3bd32 100644 --- a/bemani/backend/sdvx/factory.py +++ b/bemani/backend/sdvx/factory.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional +from typing import Optional from bemani.backend.base import Base, Factory from bemani.backend.sdvx.booth import SoundVoltexBooth @@ -8,7 +8,7 @@ from bemani.backend.sdvx.gravitywars_s1 import SoundVoltexGravityWarsSeason1 from bemani.backend.sdvx.gravitywars_s2 import SoundVoltexGravityWarsSeason2 from bemani.backend.sdvx.heavenlyhaven import SoundVoltexHeavenlyHaven from bemani.common import Model, VersionConstants -from bemani.data import Data +from bemani.data import Config, Data class SoundVoltexFactory(Factory): @@ -26,7 +26,7 @@ class SoundVoltexFactory(Factory): Base.register(gamecode, SoundVoltexFactory) @classmethod - def create(cls, data: Data, config: Dict[str, Any], model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: + def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]: def version_from_date(date: int) -> Optional[int]: if date < 2013060500: diff --git a/bemani/data/__init__.py b/bemani/data/__init__.py index da03e2e..c2fa389 100644 --- a/bemani/data/__init__.py +++ b/bemani/data/__init__.py @@ -1,3 +1,4 @@ +from bemani.data.config import Config from bemani.data.data import Data, DBCreateException from bemani.data.exceptions import ScoreSaveException from bemani.data.types import User, Achievement, Machine, Arcade, Score, Attempt, News, Link, Song, Event, Server, Client, UserID, ArcadeID @@ -6,6 +7,7 @@ from bemani.data.triggers import Triggers __all__ = [ + "Config", "Data", "DBCreateException", "ScoreSaveException", diff --git a/bemani/data/config.py b/bemani/data/config.py new file mode 100644 index 0000000..b7067cc --- /dev/null +++ b/bemani/data/config.py @@ -0,0 +1,195 @@ +import copy +import os +from sqlalchemy.engine import Engine # type: ignore +from typing import Any, Dict, Optional, Set + +from bemani.common.constants import GameConstants +from bemani.data.types import ArcadeID + + +class Database: + def __init__(self, parent_config: "Config") -> None: + self.__config = parent_config + + @property + def address(self) -> str: + return str(self.__config.get('database', {}).get('address', 'localhost')) + + @property + def database(self) -> str: + return str(self.__config.get('database', {}).get('database', 'bemani')) + + @property + def user(self) -> str: + return str(self.__config.get('database', {}).get('user', 'bemani')) + + @property + def password(self) -> str: + return str(self.__config.get('database', {}).get('password', 'bemani')) + + @property + def engine(self) -> Engine: + engine = self.__config.get('database', {}).get('engine') + if engine is None: + raise Exception("Config object is not instantiated properly, no SQLAlchemy engine present!") + if not isinstance(engine, Engine): + raise Exception("Config object is not instantiated properly, engine property is not a SQLAlchemy Engine!") + return engine + + @property + def read_only(self) -> bool: + return bool(self.__config.get('database', {}).get('read_only', False)) + + +class Server: + def __init__(self, parent_config: "Config") -> None: + self.__config = parent_config + + @property + def address(self) -> str: + return str(self.__config.get('server', {}).get('address', '127.0.0.1')) + + @property + def keepalive(self) -> str: + return str(self.__config.get('server', {}).get('keepalive', self.address)) + + @property + def port(self) -> int: + return int(self.__config.get('server', {}).get('port', 80)) + + @property + def https(self) -> bool: + return bool(self.__config.get('server', {}).get('https', False)) + + @property + def uri(self) -> Optional[str]: + uri = self.__config.get('server', {}).get('uri') + return str(uri) if uri else None + + @property + def redirect(self) -> Optional[str]: + redirect = self.__config.get('server', {}).get('redirect') + return str(redirect) if redirect else None + + @property + def enforce_pcbid(self) -> bool: + return bool(self.__config.get('server', {}).get('enforce_pcbid', False)) + + +class Client: + def __init__(self, parent_config: "Config") -> None: + self.__config = parent_config + + @property + def address(self) -> str: + address = self.__config.get('client', {}).get('address') + if address is None: + raise Exception("Config object is not instantiated properly, no client address present!") + return str(address) + + +class Machine: + def __init__(self, parent_config: "Config") -> None: + self.__config = parent_config + + @property + def pcbid(self) -> str: + pcbid = self.__config.get('machine', {}).get('pcbid') + if pcbid is None: + raise Exception("Config object is not instantiated properly, no machine pcbid present!") + return str(pcbid) + + @property + def arcade(self) -> Optional[ArcadeID]: + return self.__config.get('machine', {}).get('arcade') + + +class PASELI: + def __init__(self, parent_config: "Config") -> None: + self.__config = parent_config + + @property + def enabled(self) -> bool: + return bool(self.__config.get('paseli', {}).get('enabled', False)) + + @property + def infinite(self) -> bool: + return bool(self.__config.get('paseli', {}).get('infinite', False)) + + +class WebHooks: + def __init__(self, parent_config: "Config") -> None: + self.discord = DiscordWebHooks(parent_config) + + +class DiscordWebHooks: + def __init__(self, parent_config: "Config") -> None: + self.__config = parent_config + + def __getitem__(self, key: GameConstants) -> Optional[str]: + uri = self.__config.get('webhooks', {}).get('discord', {}).get(key.value) + return str(uri) if uri else None + + +class Config(dict): + def __init__(self, existing_contents: Dict[str, Any] = {}) -> None: + super().__init__(existing_contents or {}) + + self.database = Database(self) + self.server = Server(self) + self.client = Client(self) + self.paseli = PASELI(self) + self.webhooks = WebHooks(self) + self.machine = Machine(self) + + def clone(self) -> "Config": + # Somehow its not possible to clone this object if an instantiated Engine is present, + # so we do a little shenanigans here. + engine = self.get('database', {}).get('engine') + if engine is not None: + self['database']['engine'] = None + + clone = Config(copy.deepcopy(self)) + + if engine is not None: + self['database']['engine'] = engine + clone['database']['engine'] = engine + + return clone + + @property + def filename(self) -> str: + filename = self.get('filename') + if filename is None: + raise Exception("Config object is not instantiated properly, no filename present!") + return os.path.abspath(str(filename)) + + @property + def support(self) -> Set[GameConstants]: + support = self.get('support') + if support is None: + raise Exception("Config object is not instantiated properly, no support list present!") + if not isinstance(support, set): + raise Exception("Config object is not instantiated properly, support property is not a Set!") + return support + + @property + def secret_key(self) -> str: + return str(self.get('secret_key', 'youdidntchangethisatalldidyou?')) + + @property + def name(self) -> str: + return str(self.get('name', 'e-AMUSEMENT Network')) + + @property + def email(self) -> str: + return str(self.get('email', 'nobody@nowhere.com')) + + @property + def cache_dir(self) -> str: + return os.path.abspath(str(self.get('cache_dir', '/tmp'))) + + @property + def event_log_duration(self) -> Optional[int]: + duration = self.get('event_log_duration') + return int(duration) if duration else None diff --git a/bemani/data/data.py b/bemani/data/data.py index 955b214..f7e4d5d 100644 --- a/bemani/data/data.py +++ b/bemani/data/data.py @@ -1,5 +1,4 @@ import os -from typing import Dict, Any import alembic.config # type: ignore from alembic.migration import MigrationContext # type: ignore @@ -14,6 +13,7 @@ from sqlalchemy.exc import ProgrammingError # type: ignore from bemani.data.api.user import GlobalUserData from bemani.data.api.game import GlobalGameData from bemani.data.api.music import GlobalMusicData +from bemani.data.config import Config from bemani.data.mysql.base import metadata from bemani.data.mysql.user import UserData from bemani.data.mysql.music import MusicData @@ -86,7 +86,7 @@ class Data: and storing data. """ - def __init__(self, config: Dict[str, Any]) -> None: + def __init__(self, config: Config) -> None: """ Initializes the data object. @@ -95,7 +95,7 @@ class Data: to initialize an internal DB connection. """ session_factory = sessionmaker( - bind=config['database']['engine'], + bind=config.database.engine, autoflush=True, autocommit=True, ) @@ -122,11 +122,11 @@ class Data: self.triggers = Triggers(config) @classmethod - def sqlalchemy_url(cls, config: Dict[str, Any]) -> str: - return f"mysql://{config['database']['user']}:{config['database']['password']}@{config['database']['address']}/{config['database']['database']}?charset=utf8mb4" + def sqlalchemy_url(cls, config: Config) -> str: + return f"mysql://{config.database.user}:{config.database.password}@{config.database.address}/{config.database.database}?charset=utf8mb4" @classmethod - def create_engine(cls, config: Dict[str, Any]) -> Engine: + def create_engine(cls, config: Config) -> Engine: return create_engine( Data.sqlalchemy_url(config), pool_recycle=3600, @@ -164,7 +164,7 @@ class Data: raise DBCreateException('Tables already created, use upgrade to upgrade schema!') metadata.create_all( - self.__config['database']['engine'].connect(), + self.__config.database.engine.connect(), checkfirst=True, ) @@ -182,7 +182,7 @@ class Data: raise DBCreateException('Tables have not been created yet, use create to create them!') # Verify that there are actual changes, and refuse to create empty migration scripts - context = MigrationContext.configure(self.__config['database']['engine'].connect(), opts={'compare_type': True}) + context = MigrationContext.configure(self.__config.database.engine.connect(), opts={'compare_type': True}) diff = compare_metadata(context, metadata) if (not allow_empty) and (len(diff) == 0): raise DBCreateException('There is nothing different between code and the DB, refusing to create migration!') diff --git a/bemani/data/mysql/base.py b/bemani/data/mysql/base.py index 99c6b9f..3614582 100644 --- a/bemani/data/mysql/base.py +++ b/bemani/data/mysql/base.py @@ -3,6 +3,7 @@ import random from typing import Dict, Any, Optional from bemani.common import Time +from bemani.data.config import Config from sqlalchemy.engine.base import Connection # type: ignore from sqlalchemy.engine import CursorResult # type: ignore @@ -39,7 +40,7 @@ class BaseData: SESSION_LENGTH = 32 - def __init__(self, config: Dict[str, Any], conn: Connection) -> None: + def __init__(self, config: Config, conn: Connection) -> None: """ Initialize any DB singleton. @@ -65,7 +66,7 @@ class BaseData: Returns: A SQLAlchemy CursorResult object. """ - if self.__config['database'].get('read_only', False): + if self.__config.database.read_only: # See if this is an insert/update/delete for write_statement in [ "insert into ", diff --git a/bemani/data/triggers.py b/bemani/data/triggers.py index 7434964..36801c7 100644 --- a/bemani/data/triggers.py +++ b/bemani/data/triggers.py @@ -1,8 +1,9 @@ from datetime import datetime from discord_webhook import DiscordWebhook, DiscordEmbed # type: ignore -from typing import Any, Dict +from typing import Dict from bemani.common.constants import GameConstants, BroadcastConstants +from bemani.data.config import Config from bemani.data.types import Song @@ -10,7 +11,7 @@ class Triggers: """ Class for broadcasting data to some outside service """ - def __init__(self, config: Dict[str, Any]): + def __init__(self, config: Config) -> None: self.config = config def __gameconst_to_series(self, game: GameConstants) -> str: @@ -28,20 +29,20 @@ class Triggers: def broadcast_score(self, data: Dict[BroadcastConstants, str], game: GameConstants, song: Song) -> None: # For now we only support discord - if self.config.get('webhooks', {}).get('discord', {}).get(game, None) is not None: + if self.config.webhooks.discord[game] is not None: self.broadcast_score_discord(data, game, song) def broadcast_score_discord(self, data: Dict[BroadcastConstants, str], game: GameConstants, song: Song) -> None: if game == GameConstants.IIDX: now = datetime.now() - webhook = DiscordWebhook(url=self.config['webhooks']['discord'][game]) + webhook = DiscordWebhook(url=self.config.webhooks.discord[game]) scoreembed = DiscordEmbed(title=f'New {self.__gameconst_to_series(game)} Score!', color='fbba08') scoreembed.set_footer(text=(now.strftime('Score was recorded on %m/%d/%y at %H:%M:%S'))) # lets give it an author - song_url = f"{self.config['server']['uri']}/{game}/topscores/{song.id}" if self.config['server']['uri'] is not None else None - scoreembed.set_author(name=self.config['name'], url=song_url) + song_url = f"{self.config.server.uri}/{game.value}/topscores/{song.id}" if self.config.server.uri is not None else None + scoreembed.set_author(name=self.config.name, url=song_url) for item, value in data.items(): inline = True if item in {BroadcastConstants.DJ_NAME, BroadcastConstants.SONG_NAME, BroadcastConstants.ARTIST_NAME, BroadcastConstants.PLAY_STATS_HEADER}: diff --git a/bemani/frontend/account/account.py b/bemani/frontend/account/account.py index a4d1b48..ca9d629 100644 --- a/bemani/frontend/account/account.py +++ b/bemani/frontend/account/account.py @@ -28,7 +28,7 @@ def login() -> Response: return Response(render_template('account/login.html', **{'title': 'Log In', 'show_navigation': False, 'username': username})) if g.data.local.user.validate_password(userid, password): - aes = AESCipher(g.config['secret_key']) + aes = AESCipher(g.config.secret_key) sessionID = g.data.local.user.create_session(userid, expiration=90 * 86400) response = make_response(redirect(url_for('home_pages.viewhome'))) response.set_cookie( @@ -127,7 +127,7 @@ def register() -> Response: g.data.local.user.update_password(userid, password1) # Now, log them into that created account! - aes = AESCipher(g.config['secret_key']) + aes = AESCipher(g.config.secret_key) sessionID = g.data.local.user.create_session(userid) success('Successfully registered account!') response = make_response(redirect(url_for('home_pages.viewhome'))) diff --git a/bemani/frontend/admin/admin.py b/bemani/frontend/admin/admin.py index 68c549b..3022c0d 100644 --- a/bemani/frontend/admin/admin.py +++ b/bemani/frontend/admin/admin.py @@ -156,9 +156,9 @@ def viewevents() -> Response: 'refresh': url_for('admin_pages.listevents', since=-1), 'backfill': url_for('admin_pages.backfillevents', until=-1), 'viewuser': url_for('admin_pages.viewuser', userid=-1), - 'jubeatsong': url_for('jubeat_pages.viewtopscores', musicid=-1) if GameConstants.JUBEAT in g.config['support'] else None, - 'iidxsong': url_for('iidx_pages.viewtopscores', musicid=-1) if GameConstants.IIDX in g.config['support'] else None, - 'pnmsong': url_for('popn_pages.viewtopscores', musicid=-1) if GameConstants.POPN_MUSIC in g.config['support'] else None, + 'jubeatsong': url_for('jubeat_pages.viewtopscores', musicid=-1) if GameConstants.JUBEAT in g.config.support else None, + 'iidxsong': url_for('iidx_pages.viewtopscores', musicid=-1) if GameConstants.IIDX in g.config.support else None, + 'pnmsong': url_for('popn_pages.viewtopscores', musicid=-1) if GameConstants.POPN_MUSIC in g.config.support else None, }, ) @@ -214,8 +214,8 @@ def viewarcades() -> Response: { 'arcades': [format_arcade(arcade) for arcade in g.data.local.machine.get_all_arcades()], 'usernames': g.data.local.user.get_all_usernames(), - 'paseli_enabled': g.config['paseli']['enabled'], - 'paseli_infinite': g.config['paseli']['infinite'], + 'paseli_enabled': g.config.paseli.enabled, + 'paseli_infinite': g.config.paseli.infinite, 'mask_services_url': False, }, { @@ -252,7 +252,7 @@ def viewmachines() -> Response: GameConstants.SDVX.value: 'SDVX', }, 'games': games, - 'enforcing': g.config['server']['enforce_pcbid'], + 'enforcing': g.config.server.enforce_pcbid, }, { 'generatepcbid': url_for('admin_pages.generatepcbid'), diff --git a/bemani/frontend/app.py b/bemani/frontend/app.py index 9edbfcf..c32962d 100644 --- a/bemani/frontend/app.py +++ b/bemani/frontend/app.py @@ -8,7 +8,7 @@ from flask_caching import Cache # type: ignore from functools import wraps from bemani.common import AESCipher, GameConstants -from bemani.data import Data +from bemani.data import Config, Data from bemani.frontend.types import g from bemani.frontend.templates import templates_location from bemani.frontend.static import static_location @@ -18,7 +18,7 @@ app = Flask( template_folder=templates_location, static_folder=static_location, ) -config: Dict[str, Any] = {} +config = Config() @app.before_request @@ -26,7 +26,7 @@ def before_request() -> None: global config g.cache = Cache(app, config={ 'CACHE_TYPE': 'filesystem', - 'CACHE_DIR': config['cache_dir'], + 'CACHE_DIR': config.cache_dir, }) if request.endpoint in ['jsx', 'static']: # This is just serving cached compiled frontends, skip loading from DB @@ -37,7 +37,7 @@ def before_request() -> None: g.sessionID = None g.userID = None try: - aes = AESCipher(config['secret_key']) + aes = AESCipher(config.secret_key) sessionID = aes.decrypt(request.cookies.get('SessionID')) except Exception: sessionID = None @@ -254,14 +254,24 @@ def navigation() -> Dict[str, Any]: return False # Look up the logged in user ID. - if g.userID is not None: - user = g.data.local.user.get_user(g.userID) - profiles = g.data.local.user.get_games_played(g.userID) - else: + try: + if g.userID is not None: + user = g.data.local.user.get_user(g.userID) + profiles = g.data.local.user.get_games_played(g.userID) + else: + return { + 'components': components, + 'any': jinja2_any, + } + except AttributeError: + # If we are trying to render a 500 error and we couldn't even run the + # before request, we won't have a userID object on g. So, just give + # up and refuse to render any navigation. return { 'components': components, 'any': jinja2_any, } + pages: List[Dict[str, Any]] = [] # Landing page @@ -272,7 +282,7 @@ def navigation() -> Dict[str, Any]: }, ) - if GameConstants.BISHI_BASHI in g.config['support']: + if GameConstants.BISHI_BASHI in g.config.support: # BishiBashi pages bishi_entries = [] if len([p for p in profiles if p[0] == GameConstants.BISHI_BASHI]) > 0: @@ -301,7 +311,7 @@ def navigation() -> Dict[str, Any]: }, ) - if GameConstants.DDR in g.config['support']: + if GameConstants.DDR in g.config.support: # DDR pages ddr_entries = [] if len([p for p in profiles if p[0] == GameConstants.DDR]) > 0: @@ -350,7 +360,7 @@ def navigation() -> Dict[str, Any]: }, ) - if GameConstants.IIDX in g.config['support']: + if GameConstants.IIDX in g.config.support: # IIDX pages iidx_entries = [] if len([p for p in profiles if p[0] == GameConstants.IIDX]) > 0: @@ -399,7 +409,7 @@ def navigation() -> Dict[str, Any]: }, ) - if GameConstants.JUBEAT in g.config['support']: + if GameConstants.JUBEAT in g.config.support: # Jubeat pages jubeat_entries = [] if len([p for p in profiles if p[0] == GameConstants.JUBEAT]) > 0: @@ -448,7 +458,7 @@ def navigation() -> Dict[str, Any]: }, ) - if GameConstants.MUSECA in g.config['support']: + if GameConstants.MUSECA in g.config.support: # Museca pages museca_entries = [] if len([p for p in profiles if p[0] == GameConstants.MUSECA]) > 0: @@ -493,7 +503,7 @@ def navigation() -> Dict[str, Any]: }, ) - if GameConstants.POPN_MUSIC in g.config['support']: + if GameConstants.POPN_MUSIC in g.config.support: # Pop'n Music pages popn_entries = [] if len([p for p in profiles if p[0] == GameConstants.POPN_MUSIC]) > 0: @@ -542,7 +552,7 @@ def navigation() -> Dict[str, Any]: }, ) - if GameConstants.REFLEC_BEAT in g.config['support']: + if GameConstants.REFLEC_BEAT in g.config.support: # ReflecBeat pages reflec_entries = [] if len([p for p in profiles if p[0] == GameConstants.REFLEC_BEAT]) > 0: @@ -591,7 +601,7 @@ def navigation() -> Dict[str, Any]: }, ) - if GameConstants.SDVX in g.config['support']: + if GameConstants.SDVX in g.config.support: # SDVX pages sdvx_entries = [] if len([p for p in profiles if p[0] == GameConstants.SDVX]) > 0: diff --git a/bemani/frontend/arcade/arcade.py b/bemani/frontend/arcade/arcade.py index b1251ed..61b7af9 100644 --- a/bemani/frontend/arcade/arcade.py +++ b/bemani/frontend/arcade/arcade.py @@ -153,7 +153,7 @@ def viewarcade(arcadeid: int) -> Response: 'balances': {balance[0]: balance[1] for balance in g.data.local.machine.get_balances(arcadeid)}, 'users': {user.id: user.username for user in g.data.local.user.get_all_users()}, 'events': [format_event(event) for event in g.data.local.network.get_events(arcadeid=arcadeid, event='paseli_transaction')], - 'enforcing': g.config['server']['enforce_pcbid'], + 'enforcing': g.config.server.enforce_pcbid, }, { 'refresh': url_for('arcade_pages.listarcade', arcadeid=arcadeid), diff --git a/bemani/frontend/bishi/cache.py b/bemani/frontend/bishi/cache.py index 5ed0526..0e26110 100644 --- a/bemani/frontend/bishi/cache.py +++ b/bemani/frontend/bishi/cache.py @@ -1,10 +1,8 @@ -from typing import Dict, Any - -from bemani.data import Data +from bemani.data import Config, Data class BishiBashiCache: @classmethod - def preload(cls, data: Data, config: Dict[str, Any]) -> None: + def preload(cls, data: Data, config: Config) -> None: pass diff --git a/bemani/frontend/ddr/cache.py b/bemani/frontend/ddr/cache.py index 0ea595c..e2de49c 100644 --- a/bemani/frontend/ddr/cache.py +++ b/bemani/frontend/ddr/cache.py @@ -1,8 +1,6 @@ -from typing import Dict, Any - from flask_caching import Cache # type: ignore -from bemani.data import Data +from bemani.data import Config, Data from bemani.frontend.app import app from bemani.frontend.ddr.ddr import DDRFrontend @@ -10,10 +8,10 @@ from bemani.frontend.ddr.ddr import DDRFrontend class DDRCache: @classmethod - def preload(cls, data: Data, config: Dict[str, Any]) -> None: + def preload(cls, data: Data, config: Config) -> None: cache = Cache(app, config={ 'CACHE_TYPE': 'filesystem', - 'CACHE_DIR': config['cache_dir'], + 'CACHE_DIR': config.cache_dir, }) frontend = DDRFrontend(data, config, cache) frontend.get_all_songs(force_db_load=True) diff --git a/bemani/frontend/iidx/cache.py b/bemani/frontend/iidx/cache.py index 73cf839..0e157d4 100644 --- a/bemani/frontend/iidx/cache.py +++ b/bemani/frontend/iidx/cache.py @@ -1,8 +1,6 @@ -from typing import Dict, Any - from flask_caching import Cache # type: ignore -from bemani.data import Data +from bemani.data import Config, Data from bemani.frontend.app import app from bemani.frontend.iidx.iidx import IIDXFrontend @@ -10,10 +8,10 @@ from bemani.frontend.iidx.iidx import IIDXFrontend class IIDXCache: @classmethod - def preload(cls, data: Data, config: Dict[str, Any]) -> None: + def preload(cls, data: Data, config: Config) -> None: cache = Cache(app, config={ 'CACHE_TYPE': 'filesystem', - 'CACHE_DIR': config['cache_dir'], + 'CACHE_DIR': config.cache_dir, }) frontend = IIDXFrontend(data, config, cache) frontend.get_all_songs(force_db_load=True) diff --git a/bemani/frontend/jubeat/cache.py b/bemani/frontend/jubeat/cache.py index 1304101..daa966f 100644 --- a/bemani/frontend/jubeat/cache.py +++ b/bemani/frontend/jubeat/cache.py @@ -1,8 +1,6 @@ -from typing import Dict, Any - from flask_caching import Cache # type: ignore -from bemani.data import Data +from bemani.data import Config, Data from bemani.frontend.app import app from bemani.frontend.jubeat.jubeat import JubeatFrontend @@ -10,10 +8,10 @@ from bemani.frontend.jubeat.jubeat import JubeatFrontend class JubeatCache: @classmethod - def preload(cls, data: Data, config: Dict[str, Any]) -> None: + def preload(cls, data: Data, config: Config) -> None: cache = Cache(app, config={ 'CACHE_TYPE': 'filesystem', - 'CACHE_DIR': config['cache_dir'], + 'CACHE_DIR': config.cache_dir, }) frontend = JubeatFrontend(data, config, cache) frontend.get_all_songs(force_db_load=True) diff --git a/bemani/frontend/museca/cache.py b/bemani/frontend/museca/cache.py index 372958b..48c217a 100644 --- a/bemani/frontend/museca/cache.py +++ b/bemani/frontend/museca/cache.py @@ -1,8 +1,6 @@ -from typing import Dict, Any - from flask_caching import Cache # type: ignore -from bemani.data import Data +from bemani.data import Config, Data from bemani.frontend.app import app from bemani.frontend.museca.museca import MusecaFrontend @@ -10,10 +8,10 @@ from bemani.frontend.museca.museca import MusecaFrontend class MusecaCache: @classmethod - def preload(cls, data: Data, config: Dict[str, Any]) -> None: + def preload(cls, data: Data, config: Config) -> None: cache = Cache(app, config={ 'CACHE_TYPE': 'filesystem', - 'CACHE_DIR': config['cache_dir'], + 'CACHE_DIR': config.cache_dir, }) frontend = MusecaFrontend(data, config, cache) frontend.get_all_songs(force_db_load=True) diff --git a/bemani/frontend/popn/cache.py b/bemani/frontend/popn/cache.py index b80dbe3..2df1d5f 100644 --- a/bemani/frontend/popn/cache.py +++ b/bemani/frontend/popn/cache.py @@ -1,8 +1,6 @@ -from typing import Dict, Any - from flask_caching import Cache # type: ignore -from bemani.data import Data +from bemani.data import Config, Data from bemani.frontend.app import app from bemani.frontend.popn.popn import PopnMusicFrontend @@ -10,10 +8,10 @@ from bemani.frontend.popn.popn import PopnMusicFrontend class PopnMusicCache: @classmethod - def preload(cls, data: Data, config: Dict[str, Any]) -> None: + def preload(cls, data: Data, config: Config) -> None: cache = Cache(app, config={ 'CACHE_TYPE': 'filesystem', - 'CACHE_DIR': config['cache_dir'], + 'CACHE_DIR': config.cache_dir, }) frontend = PopnMusicFrontend(data, config, cache) frontend.get_all_songs(force_db_load=True) diff --git a/bemani/frontend/reflec/cache.py b/bemani/frontend/reflec/cache.py index 03ef745..a57d5f9 100644 --- a/bemani/frontend/reflec/cache.py +++ b/bemani/frontend/reflec/cache.py @@ -1,8 +1,6 @@ -from typing import Dict, Any - from flask_caching import Cache # type: ignore -from bemani.data import Data +from bemani.data import Config, Data from bemani.frontend.app import app from bemani.frontend.reflec.reflec import ReflecBeatFrontend @@ -10,10 +8,10 @@ from bemani.frontend.reflec.reflec import ReflecBeatFrontend class ReflecBeatCache: @classmethod - def preload(cls, data: Data, config: Dict[str, Any]) -> None: + def preload(cls, data: Data, config: Config) -> None: cache = Cache(app, config={ 'CACHE_TYPE': 'filesystem', - 'CACHE_DIR': config['cache_dir'], + 'CACHE_DIR': config.cache_dir, }) frontend = ReflecBeatFrontend(data, config, cache) frontend.get_all_songs(force_db_load=True) diff --git a/bemani/frontend/sdvx/cache.py b/bemani/frontend/sdvx/cache.py index fbeb643..d6d4c2f 100644 --- a/bemani/frontend/sdvx/cache.py +++ b/bemani/frontend/sdvx/cache.py @@ -1,8 +1,6 @@ -from typing import Dict, Any - from flask_caching import Cache # type: ignore -from bemani.data import Data +from bemani.data import Config, Data from bemani.frontend.app import app from bemani.frontend.sdvx.sdvx import SoundVoltexFrontend @@ -10,10 +8,10 @@ from bemani.frontend.sdvx.sdvx import SoundVoltexFrontend class SoundVoltexCache: @classmethod - def preload(cls, data: Data, config: Dict[str, Any]) -> None: + def preload(cls, data: Data, config: Config) -> None: cache = Cache(app, config={ 'CACHE_TYPE': 'filesystem', - 'CACHE_DIR': config['cache_dir'], + 'CACHE_DIR': config.cache_dir, }) frontend = SoundVoltexFrontend(data, config, cache) frontend.get_all_songs(force_db_load=True) diff --git a/bemani/frontend/templates/admin/settings.html b/bemani/frontend/templates/admin/settings.html index 9821198..c5d71d0 100644 --- a/bemani/frontend/templates/admin/settings.html +++ b/bemani/frontend/templates/admin/settings.html @@ -1,17 +1,17 @@ {% extends "base.html" %} {% block content %}
- To change any of these settings, edit server.yaml. + To change any of these settings, edit {{ config.filename }}.

Database Settings

Host
-
{{ config['database']['address'] }}
+
{{ config.database.address }}
Database
-
{{ config['database']['database'] }}
+
{{ config.database.database }}
User
-
{{ config['database']['user'] }}
+
{{ config.database.user }}
Password
••••••
@@ -20,44 +20,51 @@

Backend Settings

Host
-
{{ config['server']['address'] }}
+
{{ config.server.address }}
Port
-
{{ config['server']['port'] }}
+
{{ config.server.port }}
HTTPS
-
{{ 'active' if config['server']['https'] else 'inactive' }}
+
{{ 'active' if config.server.https else 'inactive' }}
Keepalive Address
-
{{ config['server']['keepalive'] }}
+
{{ config.server.keepalive }}
HTTP Get Redirect Address
-
{{ config['server']['redirect'] if config['server']['redirect'] else 'disabled' }}
+
{{ config.server.redirect or 'disabled' }}
+
+
+
+

Cache Settings

+
+
Cache Directory
+
{{ config.cache_dir }}

Frontend Settings

Web Address
-
{{ config['server']['uri'] if config['server']['uri'] else 'http://eagate.573.jp' }}
+
{{ config.server.uri or 'http://eagate.573.jp' }}
Network Name
-
{{ config.get('name','e-AMUSEMENT Network') }}
+
{{ config.name }}

Data Exchange API Settings

Administrative Email
-
{{ config.get('email','nobody@nowhere.com') }}
+
{{ config.email }}

Server Settings

PCBID Enforcement
-
{{ 'active' if config['server']['enforce_pcbid'] else 'inactive' }}
+
{{ 'active' if config.server.enforce_pcbid else 'inactive' }}
PASELI Enabled
-
{{ 'yes' if config['paseli']['enabled'] else 'no' }} (can be overridden by arcade settings)
+
{{ 'yes' if config.paseli.enabled else 'no' }} (can be overridden by arcade settings)
Infinite PASELI Enabled
-
{{ 'yes' if config['paseli']['infinite'] else 'no' }} (can be overridden by arcade settings)
+
{{ 'yes' if config.paseli.infinite else 'no' }} (can be overridden by arcade settings)
Event Log Preservation Duration
-
{{ (config['event_log_duration']|string + ' seconds') if config['event_log_duration'] else 'infinite' }}
+
{{ (config.event_log_duration|string + ' seconds') if config.event_log_duration else 'infinite' }}
{% endblock %} diff --git a/bemani/frontend/types.py b/bemani/frontend/types.py index 379a3e3..89f5fdf 100644 --- a/bemani/frontend/types.py +++ b/bemani/frontend/types.py @@ -1,14 +1,14 @@ -from typing import Any, Dict, Optional, TYPE_CHECKING +from typing import Optional, TYPE_CHECKING if TYPE_CHECKING: from flask.ctx import _AppCtxGlobals from flask_caching import Cache # type: ignore - from bemani.data import Data, UserID + from bemani.data import Config, Data, UserID class RequestGlobals(_AppCtxGlobals): - config: Dict[str, Any] + config: Config cache: Cache data: Data sessionID: Optional[str] diff --git a/bemani/tests/test_BaseData.py b/bemani/tests/test_BaseData.py index 85ea92b..9e13233 100644 --- a/bemani/tests/test_BaseData.py +++ b/bemani/tests/test_BaseData.py @@ -1,5 +1,6 @@ # vim: set fileencoding=utf-8 import unittest +from unittest.mock import Mock from bemani.data.mysql.base import BaseData @@ -7,7 +8,7 @@ from bemani.data.mysql.base import BaseData class TestBaseData(unittest.TestCase): def test_basic_serialize(self) -> None: - data = BaseData({}, None) + data = BaseData(Mock(), None) testdict = { 'test1': 1, @@ -23,7 +24,7 @@ class TestBaseData(unittest.TestCase): self.assertEqual(data.deserialize(data.serialize(testdict)), testdict) def test_basic_byte_serialize(self) -> None: - data = BaseData({}, None) + data = BaseData(Mock(), None) testdict = { 'bytes': b'\x01\x02\x03\x04\x05', @@ -34,7 +35,7 @@ class TestBaseData(unittest.TestCase): self.assertEqual(data.deserialize(serialized), testdict) def test_deep_byte_serialize(self) -> None: - data = BaseData({}, None) + data = BaseData(Mock(), None) testdict = { 'sentinal': True, diff --git a/bemani/tests/test_GameData.py b/bemani/tests/test_GameData.py index cab1e0c..d4df9d5 100644 --- a/bemani/tests/test_GameData.py +++ b/bemani/tests/test_GameData.py @@ -10,7 +10,7 @@ from bemani.tests.helpers import FakeCursor class TestGameData(unittest.TestCase): def test_put_time_sensitive_settings(self) -> None: - game = GameData({}, None) + game = GameData(Mock(), None) # Verify that we catch incorrect input order with self.assertRaises(Exception) as context: diff --git a/bemani/tests/test_IIDXPendual.py b/bemani/tests/test_IIDXPendual.py index 1033707..8bcc5ec 100644 --- a/bemani/tests/test_IIDXPendual.py +++ b/bemani/tests/test_IIDXPendual.py @@ -27,14 +27,14 @@ class TestIIDXPendual(unittest.TestCase): ) def test_average_no_scores(self) -> None: - base = IIDXPendual(Mock(), {}, Mock()) + base = IIDXPendual(Mock(), Mock(), Mock()) self.assertEqual( base.delta_score([], 3), (None, None), ) def test_average_identity(self) -> None: - base = IIDXPendual(Mock(), {}, Mock()) + base = IIDXPendual(Mock(), Mock(), Mock()) self.assertEqual( base.delta_score([ self.__make_score([10, 20, 30]), @@ -43,7 +43,7 @@ class TestIIDXPendual(unittest.TestCase): ) def test_average_basic(self) -> None: - base = IIDXPendual(Mock(), {}, Mock()) + base = IIDXPendual(Mock(), Mock(), Mock()) self.assertEqual( base.delta_score([ self.__make_score([10, 20, 30]), @@ -53,7 +53,7 @@ class TestIIDXPendual(unittest.TestCase): ) def test_average_complex(self) -> None: - base = IIDXPendual(Mock(), {}, Mock()) + base = IIDXPendual(Mock(), Mock(), Mock()) self.assertEqual( base.delta_score([ self.__make_score([10, 20, 30]), @@ -64,7 +64,7 @@ class TestIIDXPendual(unittest.TestCase): ) def test_average_always_zero(self) -> None: - base = IIDXPendual(Mock(), {}, Mock()) + base = IIDXPendual(Mock(), Mock(), Mock()) ex_score, ghost = base.delta_score([ self.__make_score([random.randint(0, 10) for _ in range(64)]), self.__make_score([random.randint(0, 10) for _ in range(64)]), diff --git a/bemani/tests/test_NetworkData.py b/bemani/tests/test_NetworkData.py index afc77dc..800b3dc 100644 --- a/bemani/tests/test_NetworkData.py +++ b/bemani/tests/test_NetworkData.py @@ -11,7 +11,7 @@ from bemani.tests.helpers import FakeCursor class TestNetworkData(unittest.TestCase): def test_get_schedule_type(self) -> None: - network = NetworkData({}, None) + network = NetworkData(Mock(), None) with freeze_time('2016-01-01 12:00'): # Check daily schedule @@ -29,7 +29,7 @@ class TestNetworkData(unittest.TestCase): ) def test_should_schedule(self) -> None: - network = NetworkData({}, None) + network = NetworkData(Mock(), None) with freeze_time('2016-01-01'): # Check for should schedule if nothing in DB diff --git a/bemani/utils/config.py b/bemani/utils/config.py index 4efdd32..e217a5a 100644 --- a/bemani/utils/config.py +++ b/bemani/utils/config.py @@ -1,5 +1,5 @@ import yaml -from typing import Any, Dict, Set +from typing import Set from bemani.backend.iidx import IIDXFactory from bemani.backend.popn import PopnMusicFactory @@ -10,12 +10,13 @@ from bemani.backend.sdvx import SoundVoltexFactory from bemani.backend.reflec import ReflecBeatFactory from bemani.backend.museca import MusecaFactory from bemani.common import GameConstants -from bemani.data import Data +from bemani.data import Config, Data -def load_config(filename: str, config: Dict[str, Any]) -> None: +def load_config(filename: str, config: Config) -> None: config.update(yaml.safe_load(open(filename))) config['database']['engine'] = Data.create_engine(config) + config['filename'] = filename supported_series: Set[GameConstants] = set() for series in GameConstants: @@ -24,20 +25,20 @@ def load_config(filename: str, config: Dict[str, Any]) -> None: config['support'] = supported_series -def register_games(config: Dict[str, Any]) -> None: - if GameConstants.POPN_MUSIC in config['support']: +def register_games(config: Config) -> None: + if GameConstants.POPN_MUSIC in config.support: PopnMusicFactory.register_all() - if GameConstants.JUBEAT in config['support']: + if GameConstants.JUBEAT in config.support: JubeatFactory.register_all() - if GameConstants.IIDX in config['support']: + if GameConstants.IIDX in config.support: IIDXFactory.register_all() - if GameConstants.BISHI_BASHI in config['support']: + if GameConstants.BISHI_BASHI in config.support: BishiBashiFactory.register_all() - if GameConstants.DDR in config['support']: + if GameConstants.DDR in config.support: DDRFactory.register_all() - if GameConstants.SDVX in config['support']: + if GameConstants.SDVX in config.support: SoundVoltexFactory.register_all() - if GameConstants.REFLEC_BEAT in config['support']: + if GameConstants.REFLEC_BEAT in config.support: ReflecBeatFactory.register_all() - if GameConstants.MUSECA in config['support']: + if GameConstants.MUSECA in config.support: MusecaFactory.register_all() diff --git a/bemani/utils/dbutils.py b/bemani/utils/dbutils.py index 7b1cb60..56b4718 100644 --- a/bemani/utils/dbutils.py +++ b/bemani/utils/dbutils.py @@ -1,19 +1,19 @@ import argparse import getpass import sys -from typing import Any, Dict, Optional +from typing import Optional -from bemani.data import Data, DBCreateException +from bemani.data import Config, Data, DBCreateException from bemani.utils.config import load_config -def create(config: Dict[str, Any]) -> None: +def create(config: Config) -> None: data = Data(config) data.create() data.close() -def generate(config: Dict[str, Any], message: Optional[str], allow_empty: bool) -> None: +def generate(config: Config, message: Optional[str], allow_empty: bool) -> None: if message is None: raise Exception('Please provide a message!') data = Data(config) @@ -21,13 +21,13 @@ def generate(config: Dict[str, Any], message: Optional[str], allow_empty: bool) data.close() -def upgrade(config: Dict[str, Any]) -> None: +def upgrade(config: Config) -> None: data = Data(config) data.upgrade() data.close() -def change_password(config: Dict[str, Any], username: Optional[str]) -> None: +def change_password(config: Config, username: Optional[str]) -> None: if username is None: raise Exception('Please provide a username!') password1 = getpass.getpass('Password: ') @@ -42,7 +42,7 @@ def change_password(config: Dict[str, Any], username: Optional[str]) -> None: print(f'User {username} changed password.') -def add_admin(config: Dict[str, Any], username: Optional[str]) -> None: +def add_admin(config: Config, username: Optional[str]) -> None: if username is None: raise Exception('Please provide a username!') data = Data(config) @@ -55,7 +55,7 @@ def add_admin(config: Dict[str, Any], username: Optional[str]) -> None: print(f'User {username} gained admin rights.') -def remove_admin(config: Dict[str, Any], username: Optional[str]) -> None: +def remove_admin(config: Config, username: Optional[str]) -> None: if username is None: raise Exception('Please provide a username!') data = Data(config) @@ -96,7 +96,7 @@ def main() -> None: parser.add_argument("-c", "--config", help="Core configuration. Defaults to server.yaml", type=str, default="server.yaml") args = parser.parse_args() - config: Dict[str, Any] = {} + config = Config() load_config(args.config, config) try: diff --git a/bemani/utils/frontend.py b/bemani/utils/frontend.py index 0f70133..379425b 100644 --- a/bemani/utils/frontend.py +++ b/bemani/utils/frontend.py @@ -25,21 +25,21 @@ def register_blueprints() -> None: app.register_blueprint(arcade_pages) app.register_blueprint(home_pages) - if GameConstants.IIDX in config['support']: + if GameConstants.IIDX in config.support: app.register_blueprint(iidx_pages) - if GameConstants.POPN_MUSIC in config['support']: + if GameConstants.POPN_MUSIC in config.support: app.register_blueprint(popn_pages) - if GameConstants.JUBEAT in config['support']: + if GameConstants.JUBEAT in config.support: app.register_blueprint(jubeat_pages) - if GameConstants.BISHI_BASHI in config['support']: + if GameConstants.BISHI_BASHI in config.support: app.register_blueprint(bishi_pages) - if GameConstants.DDR in config['support']: + if GameConstants.DDR in config.support: app.register_blueprint(ddr_pages) - if GameConstants.SDVX in config['support']: + if GameConstants.SDVX in config.support: app.register_blueprint(sdvx_pages) - if GameConstants.REFLEC_BEAT in config['support']: + if GameConstants.REFLEC_BEAT in config.support: app.register_blueprint(reflec_pages) - if GameConstants.MUSECA in config['support']: + if GameConstants.MUSECA in config.support: app.register_blueprint(museca_pages) @@ -51,7 +51,7 @@ def register_games() -> None: def load_config(filename: str) -> None: global config base_load_config(filename, config) - app.secret_key = config['secret_key'] + app.secret_key = config.secret_key def main() -> None: diff --git a/bemani/utils/read.py b/bemani/utils/read.py index 6eee766..ffb3cca 100644 --- a/bemani/utils/read.py +++ b/bemani/utils/read.py @@ -17,7 +17,7 @@ from typing import Any, Dict, List, Optional, Tuple from bemani.common import GameConstants, VersionConstants, DBConstants, PEFile, Time from bemani.format import ARC, IFS, IIDXChart, IIDXMusicDB -from bemani.data import Server, Song +from bemani.data import Config, Server, Song from bemani.data.interfaces import APIProviderInterface from bemani.data.api.music import GlobalMusicData from bemani.data.api.game import GlobalGameData @@ -48,7 +48,7 @@ class ImportBase: def __init__( self, - config: Dict[str, Any], + config: Config, game: GameConstants, version: Optional[int], no_combine: bool, @@ -59,7 +59,7 @@ class ImportBase: self.update = update self.no_combine = no_combine self.__config = config - self.__engine = self.__config['database']['engine'] + self.__engine = self.__config.database.engine self.__sessionmanager = sessionmaker(self.__engine) self.__conn = self.__engine.connect() self.__session = self.__sessionmanager(bind=self.__conn) @@ -76,7 +76,7 @@ class ImportBase: if not self.__batch: raise Exception('Logic error, cannot execute outside of a batch!') - if self.__config['database'].get('read_only', False): + if self.__config.database.read_only: # See if this is an insert/update/delete for write_statement in [ "insert into ", @@ -359,7 +359,7 @@ class ImportPopn(ImportBase): def __init__( self, - config: Dict[str, Any], + config: Config, version: str, no_combine: bool, update: bool, @@ -1198,7 +1198,7 @@ class ImportJubeat(ImportBase): def __init__( self, - config: Dict[str, Any], + config: Config, version: str, no_combine: bool, update: bool, @@ -1453,7 +1453,7 @@ class ImportIIDX(ImportBase): def __init__( self, - config: Dict[str, Any], + config: Config, version: str, no_combine: bool, update: bool, @@ -2037,7 +2037,7 @@ class ImportDDR(ImportBase): def __init__( self, - config: Dict[str, Any], + config: Config, version: str, no_combine: bool, update: bool, @@ -2711,7 +2711,7 @@ class ImportSDVX(ImportBase): def __init__( self, - config: Dict[str, Any], + config: Config, version: str, no_combine: bool, update: bool, @@ -3050,7 +3050,7 @@ class ImportMuseca(ImportBase): def __init__( self, - config: Dict[str, Any], + config: Config, version: str, no_combine: bool, update: bool, @@ -3179,7 +3179,7 @@ class ImportReflecBeat(ImportBase): def __init__( self, - config: Dict[str, Any], + config: Config, version: str, no_combine: bool, update: bool, @@ -3448,7 +3448,7 @@ class ImportDanceEvolution(ImportBase): def __init__( self, - config: Dict[str, Any], + config: Config, version: str, no_combine: bool, update: bool, @@ -3652,7 +3652,7 @@ if __name__ == "__main__": raise Exception("Cannot specify both a remote server and a local file to read from!") # Load the config so we can talk to the server - config: Dict[str, Any] = {} + config = Config() load_config(args.config, config) series = None diff --git a/bemani/utils/scheduler.py b/bemani/utils/scheduler.py index ff74bbf..d1fc0c8 100644 --- a/bemani/utils/scheduler.py +++ b/bemani/utils/scheduler.py @@ -1,5 +1,5 @@ import argparse -from typing import Any, Dict, List +from typing import Any, List from bemani.backend.popn import PopnMusicFactory from bemani.backend.jubeat import JubeatFactory @@ -18,38 +18,38 @@ from bemani.frontend.sdvx import SoundVoltexCache from bemani.frontend.reflec import ReflecBeatCache from bemani.frontend.museca import MusecaCache from bemani.common import GameConstants, Time -from bemani.data import Data +from bemani.data import Config, Data from bemani.utils.config import load_config -def run_scheduled_work(config: Dict[str, Any]) -> None: +def run_scheduled_work(config: Config) -> None: data = Data(config) # Only run scheduled work for enabled components enabled_factories: List[Any] = [] enabled_caches: List[Any] = [] - if GameConstants.IIDX in config['support']: + if GameConstants.IIDX in config.support: enabled_factories.append(IIDXFactory) enabled_caches.append(IIDXCache) - if GameConstants.POPN_MUSIC in config['support']: + if GameConstants.POPN_MUSIC in config.support: enabled_factories.append(PopnMusicFactory) enabled_caches.append(PopnMusicCache) - if GameConstants.JUBEAT in config['support']: + if GameConstants.JUBEAT in config.support: enabled_factories.append(JubeatFactory) enabled_caches.append(JubeatCache) - if GameConstants.BISHI_BASHI in config['support']: + if GameConstants.BISHI_BASHI in config.support: enabled_factories.append(BishiBashiFactory) enabled_caches.append(BishiBashiCache) - if GameConstants.DDR in config['support']: + if GameConstants.DDR in config.support: enabled_factories.append(DDRFactory) enabled_caches.append(DDRCache) - if GameConstants.SDVX in config['support']: + if GameConstants.SDVX in config.support: enabled_factories.append(SoundVoltexFactory) enabled_caches.append(SoundVoltexCache) - if GameConstants.REFLEC_BEAT in config['support']: + if GameConstants.REFLEC_BEAT in config.support: enabled_factories.append(ReflecBeatFactory) enabled_caches.append(ReflecBeatCache) - if GameConstants.MUSECA in config['support']: + if GameConstants.MUSECA in config.support: enabled_factories.append(MusecaFactory) enabled_caches.append(MusecaCache) @@ -75,7 +75,7 @@ if __name__ == '__main__': args = parser.parse_args() # Set up global configuration - config: Dict[str, Any] = {} + config = Config() load_config(args.config, config) # Run out of band work diff --git a/bemani/utils/services.py b/bemani/utils/services.py index 974ac58..ed4964e 100644 --- a/bemani/utils/services.py +++ b/bemani/utils/services.py @@ -1,24 +1,22 @@ import argparse -import copy import traceback -from typing import Any, Dict from flask import Flask, request, redirect, Response, make_response from bemani.protocol import EAmuseProtocol from bemani.backend import Dispatch, UnrecognizedPCBIDException -from bemani.data import Data +from bemani.data import Config, Data from bemani.utils.config import load_config as base_load_config, register_games as base_register_games app = Flask(__name__) -config: Dict[str, Any] = {} +config = Config() @app.route('/', defaults={'path': ''}, methods=['GET']) @app.route('/', methods=['GET']) def receive_healthcheck(path: str) -> Response: global config - redirect_uri = config['server'].get('redirect') + redirect_uri = config.server.redirect if redirect_uri is None: # Return a standard status OKAY message. return Response("Services OK.") @@ -50,7 +48,7 @@ def receive_request(path: str) -> Response: # Create and format config global config - requestconfig = copy.copy(config) + requestconfig = config.clone() requestconfig['client'] = { 'address': remote_address or request.remote_addr, } diff --git a/config/server.yaml b/config/server.yaml index 76acd60..fdd6d6d 100644 --- a/config/server.yaml +++ b/config/server.yaml @@ -1,26 +1,27 @@ database: - # IP or DNS entry for MySQL instance + # IP or DNS entry for MySQL instance. address: "localhost" - # Database that will be used + # Database that will be used. database: "bemani" - # User who has full credentials to the above DB + # User who has full credentials to the above DB. user: "bemani" - # Password of said user + # Password of said user. password: "bemani" server: - # Advertised server IP or DNS entry games will connect to + # Advertised server IP or DNS entry games will connect to. address: "192.168.0.1" - # Advertised keepalive address, must be globally pingable + # Advertised keepalive address, must be globally pingable. Delete + # this to use the address above instead of a unique keepalive address. keepalive: "127.0.0.1" - # What port games will connect to + # What port on the above address games will connect to. port: 80 - # Whether games should connect over HTTPS + # Whether games should connect over HTTPS. https: False # Advertised frontend URI. Delete this to mask the frontend address. uri: "https://eagate.573.jp" # URI that users hitting the GET interface will be redirected to. - # Delete this to return an HTTP error instead. + # Delete this to return an HTTP error instead of redirecting. redirect: "https://eagate.573.jp" # Whether PCBIDs must be added to the network before games will work. enforce_pcbid: False @@ -39,31 +40,31 @@ paseli: infinite: True support: - # Bishi Bashi frontend/backend enabled + # Bishi Bashi frontend/backend enabled. bishi: True - # DDR frontend/backend enabled + # DDR frontend/backend enabled. ddr: True - # IIDX frontend/backend enabled + # IIDX frontend/backend enabled. iidx: True - # Jubeat frontend/backend enabled + # Jubeat frontend/backend enabled. jubeat: True - # Museca frontend/backend enabled + # Museca frontend/backend enabled. museca: True - # Pop'n Music frontend/backend enabled + # Pop'n Music frontend/backend enabled. pnm: True - # Reflec Beat frontend/backend enabled + # Reflec Beat frontend/backend enabled. reflec: True - # SDVX frontend/backend enabled + # SDVX frontend/backend enabled. sdvx: True -# Key used to encrypt cookies, should be unique per instance +# Key used to encrypt cookies, should be unique per network instance. secret_key: 'this_is_a_secret_please_change_me' -# Name of this network +# Name of this network. name: 'e-AMUSEMENT Network' -# Administrative contact for this network +# Administrative contact for this network. email: 'nobody@nowhere.com' -# Cache DIR, should point somewhere other than /tmp for production instances +# Cache DIR, should point somewhere other than /tmp for production instances. cache_dir: '/tmp' # Number of seconds to preserve event logs before deleting them. -# Set to zero to disable deleting logs. +# Set to zero or delete to disable deleting logs. event_log_duration: 2592000