diff --git a/v8_server/eamuse/services/__init__.py b/v8_server/eamuse/services/__init__.py index 0941157..9779816 100644 --- a/v8_server/eamuse/services/__init__.py +++ b/v8_server/eamuse/services/__init__.py @@ -1,24 +1,4 @@ -from v8_server.eamuse.services.cardmng import CardMng -from v8_server.eamuse.services.dlstatus import DLStatus -from v8_server.eamuse.services.facility import Facility -from v8_server.eamuse.services.local import Local -from v8_server.eamuse.services.message import Message -from v8_server.eamuse.services.package import Package -from v8_server.eamuse.services.pcbevent import PCBEvent -from v8_server.eamuse.services.pcbtracker import PCBTracker from v8_server.eamuse.services.services import ServiceRequest, Services, ServiceType -__all__ = [ - "CardMng", - "DLStatus", - "Facility", - "Local", - "Message", - "PCBEvent", - "PCBTracker", - "Package", - "ServiceRequest", - "ServiceType", - "Services", -] +__all__ = ["ServiceRequest", "ServiceType", "Services"] diff --git a/v8_server/eamuse/services/cardmng.py b/v8_server/eamuse/services/cardmng.py index 1e6bf11..dfc9405 100644 --- a/v8_server/eamuse/services/cardmng.py +++ b/v8_server/eamuse/services/cardmng.py @@ -1,10 +1,12 @@ from typing import Any, Dict, List, Optional +from lxml import etree + from v8_server import db from v8_server.eamuse.services.services import ServiceRequest from v8_server.eamuse.xml.utils import get_xml_attrib, load_xml_template -from v8_server.model.user import Card, Profile, RefID, User, UserAccount -from v8_server.utils.convert import bool_to_int as btoi +from v8_server.model.user import Card, RefID, User, UserAccount +from v8_server.utils.convert import bool_to_int as btoi, int_to_bool as itob class CardStatus(object): @@ -19,38 +21,40 @@ class CardStatus(object): INVALID_PIN = 116 -class CardMng(object): +class Inquire(object): """ - Handle the CardMng (Card Manage) request. + Handle the CardMng Inquire request. - This is for supporting eAmuse card interaction. + Given the Card ID, determine if we have a brand new user or a returning one. + + + + """ - # Methods - INQUIRE = "inquire" - GETREFID = "getrefid" - AUTHPASS = "authpass" - BINDMODEL = "bindmodel" - GETKEEPSPAN = "getkeepspan" - GETDATALIST = "getdatalist" + def __init__(self, req: ServiceRequest) -> None: + self.cardid = get_xml_attrib(req.xml[0], "cardid") + self.cardtype = int(get_xml_attrib(req.xml[0], "cardtype")) + self.update = int(get_xml_attrib(req.xml[0], "update")) - @classmethod - def inquire(cls, req: ServiceRequest): + # Model doesn't always exist in the request, unsure what it's used for + model_value = get_xml_attrib(req.xml[0], "model") + self.model = model_value if model_value != "None" else None + + def response(self) -> etree: """ - Handle a Card Manage Inquire Request - - Either the given card id is a brand new user, or a returning user. - Modifyable XML Text Replacements: refid: The RefID.refid value for an existing user newflag: Set to true for a new user, else 0 binded: Set to true if the user has a profile/account, else 0 status: See CardMng status consts at the top of this class """ - - # Grab the card id - cardid = get_xml_attrib(req.xml[0], "cardid") - # New unregistered user args: Dict[str, Any] = { "newflag": 1, @@ -69,7 +73,7 @@ class CardMng(object): } # We have a returning user - if (user := User.from_cardid(cardid)) is not None: + if (user := User.from_cardid(self.cardid)) is not None: refid = RefID.from_userid(user.userid) bound = UserAccount.from_userid(user.userid) is not None @@ -89,19 +93,44 @@ class CardMng(object): "cardmng", "inquire", args, drop_attributes=drop_attributes ) - @classmethod - def getrefid(cls, req: ServiceRequest): - # Grab the card id and pin - cardid = get_xml_attrib(req.xml[0], "cardid") - pin = get_xml_attrib(req.xml[0], "passwd") + def __repr__(self) -> str: + return ( + f'CardMng.Inquire' + ) + +class Getrefid(object): + """ + Handle the CardMng Getrefid request. + + Given the Card ID, password, etc, generate a new RefID for the user and return it + + + + + """ + + def __init__(self, req: ServiceRequest) -> None: + self.cardid = get_xml_attrib(req.xml[0], "cardid") + self.cardtype = int(get_xml_attrib(req.xml[0], "cardtype")) + self.newflag = itob(int(get_xml_attrib(req.xml[0], "newflag"))) + self.passwd = get_xml_attrib(req.xml[0], "passwd") + + def response(self) -> etree: # Create a new user object with the given pin - user = User(pin=pin) + user = User(pin=self.passwd) db.session.add(user) db.session.commit() # Create the card, tie it to the user account - card = Card(cardid=cardid, userid=user.userid) + card = Card(cardid=self.cardid, userid=user.userid) db.session.add(card) db.session.commit() @@ -110,42 +139,79 @@ class CardMng(object): return load_xml_template("cardmng", "getrefid", {"refid": refid.refid}) - @classmethod - def authpass(cls, req: ServiceRequest): - """""" - # Grab the refid and pin - refid_str = get_xml_attrib(req.xml[0], "refid") - pin = get_xml_attrib(req.xml[0], "pass") + def __repr__(self) -> str: + return ( + f'CardMng.Getrefid' + ) + +class Authpass(object): + """ + Handle the CardMng Authpass request. + + Given the RefID, and password, verify that the password given is correct for that + user + + + + + """ + + def __init__(self, req: ServiceRequest) -> None: + self.passwd = get_xml_attrib(req.xml[0], "pass") + self.refid = get_xml_attrib(req.xml[0], "refid") + + def response(self) -> etree: # Grab the refid - refid = db.session.query(RefID).filter(RefID.refid == refid_str).one_or_none() + refid = db.session.query(RefID).filter(RefID.refid == self.refid).one_or_none() if refid is None: raise Exception("RefID Is None Here!") # Check if the pin is valid for the user user = refid.user - status = CardStatus.SUCCESS if user.pin == pin else CardStatus.INVALID_PIN + status = ( + CardStatus.SUCCESS if user.pin == self.passwd else CardStatus.INVALID_PIN + ) return load_xml_template("cardmng", "authpass", {"status": status}) - @classmethod - def bindmodel(cls, req: ServiceRequest): - """""" - # Grab the refid - refid_str = get_xml_attrib(req.xml[0], "refid") - refid = db.session.query(RefID).filter(RefID.refid == refid_str).one_or_none() + def __repr__(self) -> str: + return f'CardMng.Authpass' + + +class Bindmodel(object): + """ + Handle the CardMng Bindmodel request. + + Given the RefID, and a newflag, bind the user to the current game. + + XXX: Partially unsure what this actually needs to do + + + + + """ + + def __init__(self, req: ServiceRequest) -> None: + self.refid = get_xml_attrib(req.xml[0], "refid") + self.newflag = itob(int(get_xml_attrib(req.xml[0], "newflag"))) + + def response(self) -> etree: + refid = db.session.query(RefID).filter(RefID.refid == self.refid).one_or_none() if refid is None: raise Exception("RefID is None Here!") - # Just bind some garbage here for now - profile = Profile(refid=refid.refid, data={"data": "something"}) - db.session.add(profile) - db.session.commit() - return load_xml_template("cardmng", "bindmodel", {"refid": refid.refid}) + def __repr__(self) -> str: + return f'CardMng.Bindmodel' + + +# TODO: figure out if these are actually used or not +''' @classmethod def getkeepspan(cls): """ @@ -161,3 +227,4 @@ class CardMng(object): Unclear what this method does, return a dummy response """ return load_xml_template("cardmng", "cardmng") +''' diff --git a/v8_server/eamuse/services/cardutil.py b/v8_server/eamuse/services/cardutil.py new file mode 100644 index 0000000..76417c9 --- /dev/null +++ b/v8_server/eamuse/services/cardutil.py @@ -0,0 +1,161 @@ +import logging +from typing import Any, Dict, List, Optional + +from lxml import etree + +from v8_server import db +from v8_server.eamuse.services.services import ServiceRequest +from v8_server.eamuse.xml.utils import get_xml_attrib, load_xml_template +from v8_server.model.user import User, UserAccount +from v8_server.utils.convert import int_to_bool as itob + + +logger = logging.getLogger(__name__) + + +class Card(object): + """ + Cardutil.Check.Card object + """ + + def __init__(self, root: etree) -> None: + self.no = int(get_xml_attrib(root, "no")) + self.refid = root.find("refid").text + self.uid = root.find("uid").text + + def __repr__(self) -> str: + return f'Card' + + +class CheckStatus(object): + """ + Status values for the Cardutil.Check response + """ + + NEW_USER = 0 + UNKNOWN = 1 # XXX: Unsure if this is used anywhere for anything + EXISTING_USER = 2 + + +class Check(object): + """ + Handle the Cardutil.Check request. + + Checks to see if the card belongs to a new user or an existing registered user. + + + + + E9D2DD02072F05C5 + E00401007F7AD7A4 + + + + """ + + def __init__(self, req: ServiceRequest) -> None: + self.card = Card(req.xml[0].find("card")) + + def __repr__(self) -> str: + return f"Cardutil.Check" + + def response(self) -> etree: + user = User.from_refid(self.card.refid) + + new_user = ( + user is None or (account := UserAccount.from_userid(user.userid)) is None + ) + + # New User + args: Dict[str, Any] = {"state": CheckStatus.NEW_USER} + drop_children: Optional[Dict[str, List[str]]] = { + "cardutil/card": [ + "name", + "gdp", + "skill", + "all_skill", + "chara", + "syogo", + "penalty", + ], + } + + # Existing User + if not new_user and account is not None: + args = { + "state": CheckStatus.EXISTING_USER, + "name": account.name, + "chara": account.chara, + } + drop_children = None + + return load_xml_template("cardutil", "check", args, drop_children=drop_children) + + +class Data(object): + """ + Cardutil.Regist.Data object + """ + + def __init__(self, root: etree) -> None: + self.no = int(get_xml_attrib(root, "no")) + self.refid = root.find("refid").text + self.name = root.find("name").text + self.chara = int(root.find("chara").text) + self.uid = root.find("uid").text + self.cabid = int(root.find("cabid").text) + self.is_succession = itob(int(root.find("is_succession"))) + + def __repr__(self) -> str: + return ( + f'Data" + ) + + +class Regist(object): + """ + Handle the Cardutil Regist request. + + Register the data for a new user. + + + + + E9D2DD02072F05C5 + AAAA + 0 + E00401007F7AD7A4 + 1 + 0 + + + + """ + + def __init__(self, req: ServiceRequest) -> None: + self.data = Data(req.xml[0].find("data")) + + def __repr__(self) -> str: + return f"Cardutil.Regist" + + def response(self) -> etree: + user = User.from_refid(self.data.refid) + if user is None: + raise Exception("This user should theoretically exist here") + if user.card.cardid != self.data.uid: + raise Exception( + f"Card ID is incorrect: {user.card.cardid} != {self.data.uid}" + ) + + user_account = UserAccount( + userid=user.userid, + name=self.data.name, + chara=self.data.chara, + is_succession=self.data.is_succession, + ) + db.session.add(user_account) + db.session.commit() + + return load_xml_template("cardutil", "regist") diff --git a/v8_server/eamuse/services/customize.py b/v8_server/eamuse/services/customize.py new file mode 100644 index 0000000..5a5bcbc --- /dev/null +++ b/v8_server/eamuse/services/customize.py @@ -0,0 +1,82 @@ +import logging + +from lxml import etree + +from v8_server.eamuse.services.services import ServiceRequest +from v8_server.eamuse.xml.utils import get_xml_attrib, load_xml_template + + +logger = logging.getLogger(__name__) + + +class Get(object): + """ + Customize.Regist.Player.Syogodata.Get object + """ + + def __init__(self, root: etree) -> None: + self.syogo = [int(x.text) for x in root.findall("syogo")] + + def __repr__(self) -> str: + return f"Get" + + +class Syogodata(object): + """ + Customize.Regist.Player.Syogodata object + """ + + def __init__(self, root: etree) -> None: + self.get = Get(root.find("get")) + + def __repr__(self) -> str: + return f"Syogodata" + + +class Player(object): + """ + Customize.Regist.Player object + """ + + def __init__(self, root: etree) -> None: + self.no = int(get_xml_attrib(root, "no")) + self.refid = root.find("refid").text + self.syogodata = Syogodata(root.find("syogodata")) + + def __repr__(self) -> str: + return ( + f'Player" + ) + + +class Regist(object): + """ + Handle the Customize Regist request. + + Possibly just used to save some customizations for the user after the game ends. + Such as giving the user a new title. + + + + + E9D2DD02072F05C5 + + + 4 + 0 + + + + + + """ + + def __init__(self, req: ServiceRequest) -> None: + self.players = [Player(x) for x in req.xml[0].findall("player")] + + def __repr__(self) -> str: + return f"Customize.Regist" + + def response(self) -> etree: + return load_xml_template("customize", "regist") diff --git a/v8_server/eamuse/services/demodata.py b/v8_server/eamuse/services/demodata.py new file mode 100644 index 0000000..66bb121 --- /dev/null +++ b/v8_server/eamuse/services/demodata.py @@ -0,0 +1,72 @@ +import logging +from datetime import datetime + +from lxml import etree + +from v8_server.eamuse.services.services import ServiceRequest +from v8_server.eamuse.xml.utils import load_xml_template +from v8_server.model.song import HitChart + + +logger = logging.getLogger(__name__) + + +class Shop(object): + """ + Demodata.Get.Shop object + """ + + def __init__(self, root: etree) -> None: + self.locationid = root.find("locationid").text + + def __repr__(self) -> str: + return f'Shop' + + +class Get(object): + """ + Handle the Demodata Get request. + + Return demo data for the hitchart, etc. + + + + + CA-123 + + 100 + + + """ + + DT_FMT = "%Y-%m-%d %H:%M:%S%z" + + def __init__(self, req: ServiceRequest) -> None: + self.shop = Shop(req.xml[0].find("shop")) + self.hitchart_nr = int(req.xml[0].find("hitchart_nr").text) + + def __repr__(self) -> str: + return f"Demodata.Get" + + def response(self) -> etree: + rank_items = HitChart.get_ranking(self.hitchart_nr) + + # Generate all hitchart data xml + hitchart_xml_str = "" + for rank_item in rank_items: + hitchart_xml_str += etree.tostring( + load_xml_template( + "demodata", "get.data", {"musicid": rank_item, "last1": 0} + ) + ).decode("UTF-8") + + args = { + "hitchart_nr": self.hitchart_nr, + "start": datetime.now().strftime(self.DT_FMT), + "end": datetime.now().strftime(self.DT_FMT), + "hitchart_data": hitchart_xml_str, + "division": 14, + "message": "SenPi's Kickass DrumMania V8 Machine", + } + + return load_xml_template("demodata", "get", args) diff --git a/v8_server/eamuse/services/dlstatus.py b/v8_server/eamuse/services/dlstatus.py index c2c7535..fb99534 100644 --- a/v8_server/eamuse/services/dlstatus.py +++ b/v8_server/eamuse/services/dlstatus.py @@ -1,16 +1,27 @@ +from lxml import etree + +from v8_server.eamuse.services.services import ServiceRequest from v8_server.eamuse.xml.utils import load_xml_template -class DLStatus(object): +class Progress(object): """ - Handle the DLStatus request. + Handle the DLStatus Progress request. It is unknown as to what this does. + + + + 0 + + """ - # Methods - PROGRESS = "progress" + def __init__(self, req: ServiceRequest) -> None: + self.progress_value = int(req.xml[0].find("progress").text) - @classmethod - def progress(cls): + def progress(self) -> etree: return load_xml_template("dlstatus", "progress") + + def __repr__(self) -> str: + return f"DLStatus.Progress" diff --git a/v8_server/eamuse/services/facility.py b/v8_server/eamuse/services/facility.py index b04d28c..74421ee 100644 --- a/v8_server/eamuse/services/facility.py +++ b/v8_server/eamuse/services/facility.py @@ -1,25 +1,25 @@ -from v8_server.eamuse.xml.utils import load_xml_template +from lxml import etree + +from v8_server.eamuse.services.services import ServiceRequest +from v8_server.eamuse.xml.utils import get_xml_attrib, load_xml_template -class Facility(object): +class Get(object): """ - Handle the Facility request. + Handle the Facility Get request. - The only method of note is the "get" method. This method 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. + This method 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 Arcade. - Example: - - - + + + """ - # Methods - GET = "get" + def __init__(self, req: ServiceRequest) -> None: + self.encoding = get_xml_attrib(req.xml[0], "encoding") - @classmethod - def get(cls): + def response(self) -> etree: # TODO: The facility data should be read in from a config file instead of being # hard coded here @@ -30,3 +30,6 @@ class Facility(object): "name": "SenPi Arcade", } return load_xml_template("facility", "get", args) + + def __repr__(self) -> str: + return f'Facility.Get' diff --git a/v8_server/eamuse/services/gameend.py b/v8_server/eamuse/services/gameend.py new file mode 100644 index 0000000..38ef29f --- /dev/null +++ b/v8_server/eamuse/services/gameend.py @@ -0,0 +1,476 @@ +import logging +from datetime import datetime + +from lxml import etree + +from v8_server import db +from v8_server.eamuse.services.services import ServiceRequest +from v8_server.eamuse.xml.utils import get_xml_attrib, load_xml_template +from v8_server.model.song import HitChart +from v8_server.utils.convert import int_to_bool as itob + + +logger = logging.getLogger(__name__) + + +class Gamemode(object): + """ + Gameend.Regist.Gamemode object + """ + + def __init__(self, root: etree) -> None: + self.mode = get_xml_attrib(root, "mode") + + def __repr__(self) -> str: + return f'Gamemode' + + +class Shop(object): + """ + Gameend.Regist.Shop object + """ + + def __init__(self, root: etree) -> None: + self.cabid = int(root.find("cabid").text) + + def __repr__(self) -> str: + return f"Shop" + + +class Hitchart(object): + """ + Gameend.Regist.Hitchart object + """ + + def __init__(self, root: etree) -> None: + self.musicids = [int(x.text) for x in root.findall("musicid")] + + def __repr__(self) -> str: + return f"Hitchart" + + +class Stage(object): + """ + Gameend.Regist.Modedata.Stage object + """ + + def __init__(self, root: etree) -> None: + self.no = int(root.find("no").text) + self.musicid = int(root.find("musicid").text) + self.newmusic = itob(int(root.find("newmusic").text)) + self.longmusic = itob(int(root.find("longmusic").text)) + self.kind = int(root.find("kind").text) + + def __repr__(self) -> str: + return ( + f"Stage" + ) + + +class Modedata(object): + """ + Gameend.Regist.Modedata object + """ + + def __init__(self, root: etree) -> None: + self.mode = get_xml_attrib(root, "mode") + self.stages = [Stage(x) for x in root.findall("stage")] + self.session = int(root.find("session").text) + + def __repr__(self) -> str: + return ( + f'Modedata" + ) + + +class Info(object): + """ + Gameend.Regist.Player.Playerinfo.Info object + """ + + def __init__(self, root: etree) -> None: + self.mode = int(root.find("mode").text) + self.boss = int(root.find("boss").text) + self.battle_aniv = int(root.find("battle_aniv").text) # Possible Bool? + self.free_music = int(root.find("free_music").text) + self.free_chara = int(root.find("free_chara").text) + self.event = int(root.find("event").text) + self.battle_event = int(root.find("battle_event").text) + self.champ = int(root.find("champ").text) + self.item = int(root.find("item").text) + self.quest = int(root.find("quest").text) + self.campaign = int(root.find("campaign").text) + self.gdp = int(root.find("gdp").text) + self.v7 = int(root.find("v7").text) # Possible Bool? + self.champ_result = [int(x) for x in root.find("champ_result").text.split()] + + def __repr__(self) -> str: + return ( + f"Info" + ) + + +class Customize(object): + """ + Gameend.Regist.Player.Playerinfo.Customize object + + This is potentially the mod (speed mods, dark, auto bass, etc) data + """ + + def __init__(self, root: etree) -> None: + self.shutter = int(root.find("shutter").text) + self.info_level = int(root.find("info_level").text) + self.name_disp = int(root.find("name_disp").text) # Possible Bool? + self.auto = int(root.find("auto").text) + self.random = int(root.find("random").text) + self.judge_logo = int(root.find("judge_logo").text) + self.skin = int(root.find("skin").text) + self.movie = int(root.find("movie").text) + self.attack_effect = int(root.find("attack_effect").text) + self.layout = int(root.find("layout").text) + self.target_skill = int(root.find("target_skill").text) + self.comparison = int(root.find("comparison").text) + self.meter_custom = [int(x) for x in root.find("meter_custom").text.split()] + + def __repr__(self) -> str: + return ( + f"Customize" + ) + + +class Playerinfo(object): + """ + Gameend.Regist.Player.Playerinfo object + """ + + def __init__(self, root: etree) -> None: + self.refid = root.find("refid").text + self.gdp = int(root.find("gdp").text) + self.bad_play_kind = int(root.find("bad_play_kind").text) + self.total_skill_point = int(root.find("total_skill_point").text) + self.styles = int(root.find("styles").text) + self.styles_2 = int(root.find("styles_2").text) + self.secret_music = [int(x) for x in root.find("secret_music").text.split()] + self.chara = int(root.find("chara").text) + self.secret_chara = int(root.find("secret_chara").text) + self.syogo = [int(x) for x in root.find("syogo").text.split()] + self.shutter_list = int(root.find("shutter_list").text) + self.judge_logo_list = int(root.find("judge_logo_list").text) + self.skin_list = int(root.find("skin_list").text) + self.movie_list = int(root.find("movie_list").text) + self.attack_effect_list = int(root.find("attack_effect_list").text) + self.idle_screen = int(root.find("idle_screen").text) + self.chance_point = int(root.find("chance_point").text) + self.failed_cnt = int(root.find("failed_cnt").text) + self.perfect = int(root.find("perfect").text) + self.great = int(root.find("great").text) + self.good = int(root.find("good").text) + self.poor = int(root.find("poor").text) + self.miss = int(root.find("miss").text) + self.time = int(root.find("time").text) + self.exc_clear_num = int(root.find("exc_clear_num").text) + self.full_clear_num = int(root.find("full_clear_num").text) + self.max_clear_difficulty = int(root.find("max_clear_difficulty").text) + self.max_fullcombo_difficulty = int(root.find("max_fullcombo_difficulty").text) + self.max_excellent_difficulty = int(root.find("max_excellent_difficulty").text) + self.champ_group = int(root.find("champ_group").text) + self.champ_kind = int(root.find("champ_kind").text) + self.musicid = [int(x) for x in root.find("musicid").text.split()] + self.object_musicid = [int(x) for x in root.find("object_musicid").text.split()] + self.seqmode = [int(x) for x in root.find("seqmode").text.split()] + self.flags = [int(x) for x in root.find("flags").text.split()] + self.score = [int(x) for x in root.find("score").text.split()] + self.point = [int(x) for x in root.find("point").text.split()] + self.info = Info(root.find("info")) + self.customize = Customize(root.find("customize")) + # There seems to be another `perfect` here for some reason + self.bp = int(root.find("bp").text) + self.reserv_item_list = [ + int(x) for x in root.find("reserv_item_list").text.split() + ] + self.rival_id_1 = root.find("rival_id_1").text + self.rival_id_2 = root.find("rival_id_2").text + self.rival_id_3 = root.find("rival_id_3").text + + def __repr__(self) -> str: + return ( + f'Playerinfo' + ) + + +class Playdata(object): + """ + Gameend.Regist.Player.Playdata object + """ + + def __init__(self, root: etree) -> None: + self.no = int(root.find("no").text) + self.seqmode = int(root.find("seqmode").text) + self.clear = int(root.find("clear").text) + self.auto_clear = int(root.find("auto_clear").text) + self.score = int(root.find("score").text) + self.flags = int(root.find("flags").text) + self.fullcombo = int(root.find("fullcombo").text) + self.excellent = int(root.find("excellent").text) + self.combo = int(root.find("combo").text) + self.skill_point = int(root.find("skill_point").text) + self.skill_perc = int(root.find("skill_perc").text) + self.result_rank = int(root.find("result_rank").text) + self.difficulty = int(root.find("difficulty").text) + self.combo_rate = int(root.find("combo_rate").text) + self.perfect_rate = int(root.find("perfect_rate").text) + + def __repr__(self) -> str: + return ( + f"Playdata" + ) + + +class Player(object): + """ + Gameend.Regist.Player object + """ + + def __init__(self, root: etree) -> None: + self.card = get_xml_attrib(root, "card") + self.no = int(get_xml_attrib(root, "no")) + self.playerinfo = Playerinfo(root.find("playerinfo")) + self.playdata = [Playdata(x) for x in root.findall("playdata")] + + def __repr__(self) -> str: + return ( + f'Player" + ) + + +class Regist(object): + """ + Handle the Gameend Regist request. + + + + + + 1 + + + 1849 + + + + 1 + 1849 + 1 + 0 + 0 + + 0 + + + + E9D2DD02072F05C5 + 900 + 0 + 0 + 2097152 + 0 + + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + + 0 + 1824 + 0 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 120 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 0 0 0 0 0 + + 0 0 0 0 0 0 + + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 0 0 + + 0 + 0 + + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + + + + + + + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 11 + 0 + 0 + + + + + """ + + DT_FMT = "%Y-%m-%d %H:%M:%S%z" + + def __init__(self, req: ServiceRequest) -> None: + self.gamemode = Gamemode(req.xml[0].find("gamemode")) + self.shop = Shop(req.xml[0].find("shop")) + self.hitchart = Hitchart(req.xml[0].find("hitchart")) + self.modedata = Modedata(req.xml[0].find("modedata")) + self.player = Player(req.xml[0].find("player")) + + def __repr__(self) -> str: + return ( + f"Gameend.Regist" + ) + + def response(self) -> etree: + for musicid in self.hitchart.musicids: + hc = HitChart(musicid=musicid, playdate=datetime.now()) + logger.debug(f"Saving HitChart: {hc}") + db.session.add(hc) + db.session.commit() + + # Just send back a dummy object for now + now_time = datetime.now().strftime(self.DT_FMT) + + # Generate history rounds (blank for now) + history_rounds = "" + for _ in range(0, 10): + history_rounds += etree.tostring( + load_xml_template("gameend", "regist.history.round") + ).decode("UTF-8") + + music_hist_rounds = "" + for _ in range(0, 20): + music_hist_rounds += etree.tostring( + load_xml_template("gameend", "regist.music_hist.round") + ).decode("UTF-8") + + args = { + "gamemode": self.gamemode.mode, + "player_card": self.player.card, + "player_no": self.player.no, + "now_time": now_time, + "history_rounds": history_rounds, + "music_hist_rounds": music_hist_rounds, + } + + return load_xml_template("gameend", "regist", args) diff --git a/v8_server/eamuse/services/gameinfo.py b/v8_server/eamuse/services/gameinfo.py new file mode 100644 index 0000000..a1b146d --- /dev/null +++ b/v8_server/eamuse/services/gameinfo.py @@ -0,0 +1,57 @@ +import logging + +from lxml import etree + +from v8_server.eamuse.services.services import ServiceRequest +from v8_server.eamuse.utils.crc import calculate_crc8 +from v8_server.eamuse.xml.utils import load_xml_template + + +logger = logging.getLogger(__name__) + + +class Shop(object): + """ + Gameinfo.Get.Shop object + """ + + def __init__(self, root: etree) -> None: + self.locationid = root.find("locationid").text + self.cabid = int(root.find("cabid").text) + + def __repr__(self) -> str: + return f"Shop" + + +class Get(object): + """ + Handle the Gameinfo Get request. + + Returns various game data at the start of a game session. + + + + + CA-123 + 1 + + + + """ + + def __init__(self, req: ServiceRequest) -> None: + self.shop = Shop(req.xml[0].find("shop")) + + def __repr__(self) -> str: + return f"Gameinfo.Get" + + def response(self) -> etree: + # tag is the crc8 checksum of free_music and free_chara + # I also don't actually know what these free_music/chara values mean + args = { + "free_music": 262143, + "free_chara": 1824, + "tag": calculate_crc8(str(262143 + 1824)), + "division": 14, + } + return load_xml_template("gameinfo", "get", args) diff --git a/v8_server/eamuse/services/gametop.py b/v8_server/eamuse/services/gametop.py new file mode 100644 index 0000000..c5822bb --- /dev/null +++ b/v8_server/eamuse/services/gametop.py @@ -0,0 +1,122 @@ +import logging + +from lxml import etree + +from v8_server.eamuse.services.services import ServiceRequest +from v8_server.eamuse.utils.crc import calculate_crc8 +from v8_server.eamuse.xml.utils import fill, get_xml_attrib, load_xml_template + + +logger = logging.getLogger(__name__) + + +class Request(object): + """ + Gametop.Get.Player.Request object + """ + + def __init__(self, root: etree) -> None: + self.kind = int(root.find("kind").text) + self.offset = int(root.find("offset").text) + self.music_nr = int(root.find("music_nr").text) + self.cabid = int(root.find("cabid").text) + + def __repr__(self) -> str: + return ( + f"Request" + ) + + +class Player(object): + """ + Gametop.Get.Player object + """ + + def __init__(self, root: etree) -> None: + self.card = get_xml_attrib(root, "card") + self.no = int(get_xml_attrib(root, "no")) + self.refid = root.find("refid").text + self.request = Request(root.find("request")) + + def __repr__(self) -> str: + return ( + f'Player" + ) + + +class Get(object): + """ + Handle the Gametop.Get request. + + This requests saved user data when using eAmuse. + + + + + E9D2DD02072F05C5 + + 0 + 0 + 250 + 1 + + + + + """ + + def __init__(self, req: ServiceRequest) -> None: + self.player = Player(req.xml[0].find("player")) + + def __repr__(self) -> str: + return f"Gametop.Get" + + def response(self) -> etree: + # Generate history rounds (blank for now) + history_rounds = "" + for _ in range(0, 10): + history_rounds += etree.tostring( + load_xml_template("gametop", "get.history.round") + ).decode("UTF-8") + + music_hist_rounds = "" + for _ in range(0, 20): + music_hist_rounds += etree.tostring( + load_xml_template("gametop", "get.music_hist.round") + ).decode("UTF-8") + + secret_music = fill(32) + secret_chara = 0 + tag = calculate_crc8( + str(sum(int(x) for x in secret_music.split()) + secret_chara) + ) + + args = { + "secret_music": secret_music, + "style": 2097152, + "secret_chara": secret_chara, + "tag": tag, + "history_rounds": history_rounds, + "music_hist_rounds": music_hist_rounds, + } + + return load_xml_template("gametop", "get", args) + + +# TODO: We haven't ever received a request for rival data, so we can implement this in +# the future +""" + elif req.method == cls.GAMETOP_GET_RIVAL: + response = load_xml_template( + "gametop", "get_rival", {"name": "name", "chara": 0} + ) + else: + raise Exception( + "Not sure how to handle this gametop request. " + f'method "{req.method}" is unknown for request: {req}' + ) + + return response +""" diff --git a/v8_server/eamuse/services/lobby.py b/v8_server/eamuse/services/lobby.py deleted file mode 100644 index a6b886b..0000000 --- a/v8_server/eamuse/services/lobby.py +++ /dev/null @@ -1 +0,0 @@ -# >>libshare-pj.c: xrpc_module_add(aLobby, "lobby", aLobby, &off_100404C4); diff --git a/v8_server/eamuse/services/local.py b/v8_server/eamuse/services/local.py deleted file mode 100644 index b48c3eb..0000000 --- a/v8_server/eamuse/services/local.py +++ /dev/null @@ -1,302 +0,0 @@ -import logging -from datetime import datetime -from typing import Any, Dict, List, Optional - -from lxml import etree - -from v8_server import db -from v8_server.eamuse.services.services import ServiceRequest -from v8_server.eamuse.utils.crc import calculate_crc8 -from v8_server.eamuse.xml.utils import fill, get_xml_attrib, load_xml_template -from v8_server.model.song import HitChart -from v8_server.model.user import User, UserAccount - - -logger = logging.getLogger(__name__) - - -class Local(object): - """ - Handle the Local request. - - This request handles most of the game specific requests. - """ - - # Methods - SHOPINFO = "shopinfo" - SHOPINFO_REGIST = "regist" - - DEMODATA = "demodata" - DEMODATA_GET = "get" - - CARDUTIL = "cardutil" - CARDUTIL_CHECK = "check" - CARDUTIL_REGIST = "regist" - - GAMEINFO = "gameinfo" - GAMEINFO_GET = "get" - - GAMEEND = "gameend" - GAMEEND_REGIST = "regist" - - GAMETOP = "gametop" - GAMETOP_GET = "get" - GAMETOP_GET_RIVAL = "get_rival" - - CUSTOMIZE = "customize" - CUSTOMIZE_REGIST = "regist" - - ASSERT_REPORT = "assert_report" - ASSERT_REPORT_REGIST = "regist" - - # Not sure about this one yet - INCREMENT = "increment" - - @classmethod - def customize(cls, req: ServiceRequest) -> etree: - if req.method == cls.CUSTOMIZE_REGIST: - response = load_xml_template("customize", "regist") - else: - raise Exception( - "Not sure how to handle this customize request. " - f'method "{req.method}" is unknown for request: {req}' - ) - - return response - - @classmethod - def shopinfo(cls, req: ServiceRequest) -> etree: - if req.method == cls.SHOPINFO_REGIST: - response = load_xml_template("shopinfo", "regist") - else: - raise Exception( - "Not sure how to handle this shopinfo request. " - f'method "{req.method}" is unknown for request: {req}' - ) - - return response - - @classmethod - def demodata(cls, req: ServiceRequest) -> etree: - dtfmt = "%Y-%m-%d %H:%M:%S%z" - - if req.method == cls.DEMODATA_GET: - hitchart_number = int(req.xml[0].find("hitchart_nr").text) - rank_items = HitChart.get_ranking(hitchart_number) - - # Generate all hitchart data xml - hitchart_xml_str = "" - for rank_item in rank_items: - hitchart_xml_str += etree.tostring( - load_xml_template( - "demodata", "get.data", {"musicid": rank_item, "last1": 0} - ) - ).decode("UTF-8") - - args = { - "hitchart_nr": hitchart_number, - "start": datetime.now().strftime(dtfmt), - "end": datetime.now().strftime(dtfmt), - "hitchart_data": hitchart_xml_str, - "division": 14, - "message": "SenPi's Kickass DrumMania V8 Machine", - } - - response = load_xml_template("demodata", "get", args) - else: - raise Exception( - "Not sure how to handle this demodata request. " - f'method "{req.method}" is unknown for request: {req}' - ) - - return response - - @classmethod - def cardutil(cls, req: ServiceRequest) -> etree: - if req.method == cls.CARDUTIL_CHECK: - refid = req.xml[0].find("card/refid").text - user = User.from_refid(refid) - - new_user = ( - user is None - or (account := UserAccount.from_userid(user.userid)) is None - ) - - # New User - args: Dict[str, Any] = {"state": 0} - drop_children: Optional[Dict[str, List[str]]] = { - "cardutil/card": [ - "name", - "gdp", - "skill", - "all_skill", - "chara", - "syogo", - "penalty", - ], - } - - # Existing User - if not new_user and account is not None: - args = { - "state": 2, - "name": account.name, - "chara": account.chara, - } - drop_children = None - - response = load_xml_template( - "cardutil", "check", args, drop_children=drop_children - ) - elif req.method == cls.CARDUTIL_REGIST: - root = req.xml[0].find("data") - refid = root.find("refid").text - name = root.find("name").text - chara = root.find("chara").text - cardid = root.find("uid").text - is_succession = root.find("is_succession").text - - user = User.from_refid(refid) - if user is None: - raise Exception("This user should theoretically exist here") - if user.card.cardid != cardid: - raise Exception(f"Card ID is incorrect: {user.card.cardid} != {cardid}") - - user_account = UserAccount( - userid=user.userid, - name=name, - chara=int(chara), - is_succession=True if is_succession == "1" else False, - ) - db.session.add(user_account) - db.session.commit() - - response = load_xml_template("cardutil", "regist") - else: - raise Exception( - "Not sure how to handle this cardutil request. " - f'method "{req.method}" is unknown for request: {req}' - ) - - return response - - @classmethod - def gameinfo(cls, req: ServiceRequest) -> etree: - if req.method == cls.GAMEINFO_GET: - # tag is the crc8 checksum of free_music and free_chara - # I also don't actually know what these free_music/chara values mean - args = { - "free_music": 262143, - "free_chara": 1824, - "tag": calculate_crc8(str(262143 + 1824)), - "division": 14, - } - response = load_xml_template("gameinfo", "get", args) - else: - raise Exception( - "Not sure how to handle this gameinfo request. " - f'method "{req.method}" is unknown for request: {req}' - ) - - return response - - @classmethod - def gametop(cls, req: ServiceRequest) -> etree: - if req.method == cls.GAMETOP_GET: - # Generate history rounds (blank for now) - history_rounds = "" - for _ in range(0, 10): - history_rounds += etree.tostring( - load_xml_template("gametop", "get.history.round") - ).decode("UTF-8") - - music_hist_rounds = "" - for _ in range(0, 20): - music_hist_rounds += etree.tostring( - load_xml_template("gametop", "get.music_hist.round") - ).decode("UTF-8") - - secret_music = fill(32) - secret_chara = 0 - tag = calculate_crc8( - str(sum(int(x) for x in secret_music.split()) + secret_chara) - ) - - args = { - "secret_music": secret_music, - "style": 2097152, - "secret_chara": secret_chara, - "tag": tag, - "history_rounds": history_rounds, - "music_hist_rounds": music_hist_rounds, - } - - response = load_xml_template("gametop", "get", args) - elif req.method == cls.GAMETOP_GET_RIVAL: - response = load_xml_template( - "gametop", "get_rival", {"name": "name", "chara": 0} - ) - else: - raise Exception( - "Not sure how to handle this gametop request. " - f'method "{req.method}" is unknown for request: {req}' - ) - - return response - - @classmethod - def gameend(cls, req: ServiceRequest) -> etree: - """ - Handle a GameEnd request. - - For now just save the hitchart data - """ - dtfmt = "%Y-%m-%d %H:%M:%S%z" - - if req.method == cls.GAMEEND_REGIST: - # Grab the hitchart data - hc_root = req.xml[0].find("hitchart") - - for mid in hc_root.findall("musicid"): - musicid = int(mid.text) - hc = HitChart(musicid=musicid, playdate=datetime.now()) - logger.debug(f"Saving HitChart: {hc}") - db.session.add(hc) - db.session.commit() - - # Just send back a dummy object for now - gamemode = get_xml_attrib(req.xml[0].find("gamemode"), "mode") - card = get_xml_attrib(req.xml[0].find("player"), "card") - no = get_xml_attrib(req.xml[0].find("player"), "no") - now_time = datetime.now().strftime(dtfmt) - - # Generate history rounds (blank for now) - history_rounds = "" - for _ in range(0, 10): - history_rounds += etree.tostring( - load_xml_template("gameend", "regist.history.round") - ).decode("UTF-8") - - music_hist_rounds = "" - for _ in range(0, 20): - music_hist_rounds += etree.tostring( - load_xml_template("gameend", "regist.music_hist.round") - ).decode("UTF-8") - - args = { - "gamemode": gamemode, - "player_card": card, - "player_no": no, - "now_time": now_time, - "history_rounds": history_rounds, - "music_hist_rounds": music_hist_rounds, - } - - response = load_xml_template("gameend", "regist", args) - else: - raise Exception( - "Not sure how to handle this gameend request. " - f'method "{req.method}" is unknown for request: {req}' - ) - - return response diff --git a/v8_server/eamuse/services/message.py b/v8_server/eamuse/services/message.py index cd808cc..4086d11 100644 --- a/v8_server/eamuse/services/message.py +++ b/v8_server/eamuse/services/message.py @@ -1,21 +1,25 @@ +from lxml import etree + +from v8_server.eamuse.services.services import ServiceRequest from v8_server.eamuse.xml.utils import load_xml_template -class Message(object): +class Get(object): """ - Handle the Message request. + Handle the Message Get request. It is unknown as to what this does. Possibly it's for operator messages - Example: - - - + + + """ - # Methods - GET = "get" + def __init__(self, req: ServiceRequest) -> None: + pass - @classmethod - def get(cls): + def response(self) -> etree: return load_xml_template("message", "get") + + def __repr__(self) -> str: + return "Message.Get<>" diff --git a/v8_server/eamuse/services/package.py b/v8_server/eamuse/services/package.py index 51e9dcc..33c9b08 100644 --- a/v8_server/eamuse/services/package.py +++ b/v8_server/eamuse/services/package.py @@ -1,21 +1,26 @@ -from v8_server.eamuse.xml.utils import load_xml_template +from lxml import etree + +from v8_server.eamuse.services.services import ServiceRequest +from v8_server.eamuse.xml.utils import get_xml_attrib, load_xml_template -class Package(object): +class List(object): """ - Handle the Package request. + Handle the Package List request. - This is for supporting downloading of updates. We do not support this. + This is for supporting downloading of updates. + We do not support this. - Example: - - - + + + """ - # Methods - LIST = "list" + def __init__(self, req: ServiceRequest) -> None: + self.pkgtype = get_xml_attrib(req.xml[0], "pkgtype") - @classmethod - def list(cls): + def response(self) -> etree: return load_xml_template("package", "list") + + def __repr__(self) -> str: + return f'Package.List' diff --git a/v8_server/eamuse/services/pcbevent.py b/v8_server/eamuse/services/pcbevent.py index ffb0161..7f80cfb 100644 --- a/v8_server/eamuse/services/pcbevent.py +++ b/v8_server/eamuse/services/pcbevent.py @@ -10,16 +10,15 @@ from v8_server.eamuse.xml.utils import load_xml_template logger = logging.getLogger(__name__) -class PCBEventItem(object): +class PCBEventPutItem(object): """ Contains the data for a PCBEvent Item - Example: - - K32.mode.std - 1 - - + + K32.mode.std + 1 + + """ DATE_FMT = "%b/%d/%Y-%H:%M:%S" @@ -32,39 +31,28 @@ class PCBEventItem(object): def __repr__(self) -> str: return ( - f'PCBEventItem" ) -class PCBEvent(object): +class Put(object): """ Handle the PCBEvent request. It is unknown as to what this does. - Example: - - - - 4 - - K32.mode.std - 1 - - - - K32.playcnt.t - 1 - - - - game.e - 1 - - - - + + + + 4 + + K32.mode.std + 1 + + + + """ DATE_FMT = "%b/%d/%Y-%H:%M:%S" @@ -78,27 +66,16 @@ class PCBEvent(object): self.items = [] for item in xml_root.findall("item"): - self.items.append(PCBEventItem(item)) + self.items.append(PCBEventPutItem(item)) - # Methods - PUT = "put" - - @classmethod - def put(cls, req: ServiceRequest): - """ - Example: - - - - """ # Log the event - event = PCBEvent(req) - logger.info(event) + logger.info(self) + def response(self) -> etree: return load_xml_template("pcbevent", "put") def __repr__(self) -> str: return ( - f"PCBEvent" ) diff --git a/v8_server/eamuse/services/pcbtracker.py b/v8_server/eamuse/services/pcbtracker.py index 46715f5..617a023 100644 --- a/v8_server/eamuse/services/pcbtracker.py +++ b/v8_server/eamuse/services/pcbtracker.py @@ -1,28 +1,36 @@ -from v8_server.eamuse.xml.utils import load_xml_template +from lxml import etree + +from v8_server.eamuse.services.services import ServiceRequest +from v8_server.eamuse.xml.utils import get_xml_attrib, load_xml_template from v8_server.utils.convert import bool_to_int as btoi -class PCBTracker(object): +class Alive(object): """ - Handle the PCBTracker request. + Handle the PCBTracker Alive request. - The only method of note is the "alive" method which returns whether PASELI should be - active or not for this session. + This method which returns whether PASELI should be active or not for this session. - Example: - - - + + + """ - # We don't support PASELI on GFDM: V8 - PASELI_ACTIVE = False + def __init__(self, req: ServiceRequest) -> None: + self.hardid = get_xml_attrib(req.xml[0], "hardid") + self.softid = get_xml_attrib(req.xml[0], "softid") - # Methods - ALIVE = "alive" + # Most likely you'd determine this value from the hardware id? + # Right now we don't support paseli + self.paseli_active = False - @classmethod - def alive(cls): + def response(self) -> etree: return load_xml_template( - "pcbtracker", "alive", {"ecenable": btoi(cls.PASELI_ACTIVE)} + "pcbtracker", "alive", {"ecenable": btoi(self.paseli_active)} + ) + + def __repr__(self) -> str: + return ( + f'PCBTracker.Alive" ) diff --git a/v8_server/eamuse/services/shopinfo.py b/v8_server/eamuse/services/shopinfo.py new file mode 100644 index 0000000..f200b4c --- /dev/null +++ b/v8_server/eamuse/services/shopinfo.py @@ -0,0 +1,208 @@ +import logging + +from lxml import etree + +from v8_server.eamuse.services.services import ServiceRequest +from v8_server.eamuse.xml.utils import get_xml_attrib, load_xml_template +from v8_server.utils.convert import int_to_bool as itob + + +logger = logging.getLogger(__name__) + + +class SoundOptions(object): + """ + Shopinfo.Regist.Shop.Testmode.SoundOptions object + """ + + def __init__(self, root: etree) -> None: + self.volume_bgm = int(root.find("volume_bgm").text) + self.volume_se_myself = int(root.find("volume_se_myself").text) + + def __repr__(self) -> str: + return ( + f"SoundOptions" + ) + + +class StandAlone(object): + """ + Shopinfo.Regist.Shop.Testmode.GameOptions.StandAlone object + """ + + def __init__(self, root: etree) -> None: + self.difficulty_standard = int(root.find("difficulty_standard").text) + self.max_stage_standard = int(root.find("max_stage_standard").text) + self.long_music = int(root.find("long_music").text) + + def __repr__(self) -> str: + return ( + f"StandAlone" + ) + + +class Session(object): + """ + Shopinfo.Regist.Shop.Testmode.GameOptions.Session object + """ + + def __init__(self, root: etree) -> None: + self.game_joining_period = int(root.find("game_joining_period").text) + + def __repr__(self) -> str: + return f"Session" + + +class GameSettings(object): + """ + Shopinfo.Regist.Shop.Testmode.GameOptions.GameSettings object + """ + + def __init__(self, root: etree) -> None: + self.is_shop_close_on = int(root.find("is_shop_close_on").text) + + def __repr__(self) -> str: + return f"GameSettings" + + +class GameOptions(object): + """ + Shopinfo.Regist.Shop.Testmode.GameOptions object + """ + + def __init__(self, root: etree) -> None: + self.stand_alone = StandAlone(root.find("stand_alone")) + self.session = Session(root.find("session")) + self.game_settings = GameSettings(root.find("game_settings")) + + def __repr__(self) -> str: + return ( + f"GameOptions" + ) + + +class CoinOptions(object): + """ + Shopinfo.Regist.Shop.Testmode.CoinOptions object + """ + + def __init__(self, root: etree) -> None: + self.coin_slot = int(root.find("coin_slot").text) + + def __repr__(self) -> str: + return f"CoinOptions" + + +class Bookkeeping(object): + """ + Shopinfo.Regist.Shop.Testmode.Bookkeeping object + """ + + def __init__(self, root: etree) -> None: + self.enable = itob(int(root.find("enable").text)) + + def __repr__(self) -> str: + return f"Bookkeeping" + + +class Testmode(object): + """ + Shopinfo.Regist.Shop.Testmode object + """ + + def __init__(self, root: etree) -> None: + self.send = itob(int(get_xml_attrib(root, "send"))) + self.sound_options = SoundOptions(root.find("sound_options")) + self.game_options = GameOptions(root.find("game_options")) + self.coin_options = CoinOptions(root.find("coin_options")) + self.bookkeeping = Bookkeeping(root.find("bookkeeping")) + + def __repr__(self) -> str: + return ( + f"Testmode" + ) + + +class Shop(object): + """ + Shopinfo.Regist.Shop object + """ + + def __init__(self, root: etree) -> None: + self.name = root.find("name").text + self.pref = int(root.find("pref").text) + self.systemid = root.find("systemid").text + self.softwareid = root.find("softwareid").text + self.hardwareid = root.find("hardwareid").text + self.locationid = root.find("locationid").text + self.testmode = Testmode(root.find("testmode")) + + def __repr__(self) -> str: + return ( + f'Shop" + ) + + +class Regist(object): + """ + Handle the Shopinfo Regist request. + + Possibly just used to register machine info onto the server. + + + + + + 2 + 00010203040506070809 + 012112345679 + 010074D435AAD895 + CA-123 + + + 20 + 20 + + + + 7 + 1 + 5 + + + 15 + + + 0 + + + + 1 + + + 0 + + + + + + + """ + + def __init__(self, req: ServiceRequest) -> None: + self.shop = Shop(req.xml[0].find("shop")) + + def __repr__(self) -> str: + return f"Shopinfo.Regist" + + def response(self) -> etree: + return load_xml_template("shopinfo", "regist") diff --git a/v8_server/utils/convert.py b/v8_server/utils/convert.py index 8639f47..94ff1d6 100644 --- a/v8_server/utils/convert.py +++ b/v8_server/utils/convert.py @@ -5,3 +5,12 @@ def bool_to_int(value: bool) -> int: True = 1, False = 0 """ return 1 if value else 0 + + +def int_to_bool(value: int) -> bool: + """ + Convert an integer to a boolean. + + 1 = True, 0 = False + """ + return False if value == 0 else True diff --git a/v8_server/view/index.py b/v8_server/view/index.py index e12f084..63fbf8c 100644 --- a/v8_server/view/index.py +++ b/v8_server/view/index.py @@ -1,21 +1,10 @@ +import importlib from typing import Dict, Tuple from flask import request from v8_server import app -from v8_server.eamuse.services import ( - CardMng, - DLStatus, - Facility, - Local, - Message, - Package, - PCBEvent, - PCBTracker, - ServiceRequest, - Services, - ServiceType, -) +from v8_server.eamuse.services import ServiceRequest, Services FlaskResponse = Tuple[bytes, Dict[str, str]] @@ -54,70 +43,27 @@ def service_service(route: int) -> FlaskResponse: # simple as possible req = ServiceRequest(request) - if route == ServiceType.PCBTRACKER.value: - if req.method == PCBTracker.ALIVE: - response = PCBTracker.alive() - else: - raise Exception(f"Not sure how to handle this PCBTracker Request: {req}") - elif route == ServiceType.MESSAGE.value: - if req.method == Message.GET: - response = Message.get() - else: - raise Exception(f"Not sure how to handle this Message Request: {req}") - elif route == ServiceType.PCBEVENT.value: - if req.method == PCBEvent.PUT: - response = PCBEvent.put(req) - else: - raise Exception(f"Not sure how to handle this PCBEvent Request: {req}") - elif route == ServiceType.PACKAGE.value: - if req.method == Package.LIST: - response = Package.list() - else: - raise Exception(f"Not sure how to handle this Package Request: {req}") - elif route == ServiceType.FACILITY.value: - if req.method == Facility.GET: - response = Facility.get() - else: - raise Exception(f"Not sure how to handle this Facility Request: {req}") - elif route == ServiceType.DLSTATUS.value: - if req.method == DLStatus.PROGRESS: - response = DLStatus.progress() - else: - raise Exception(f"Not sure how to handle this DLStatus Request: {req}") - elif route == ServiceType.CARDMNG.value: - if req.method == CardMng.INQUIRE: - response = CardMng.inquire(req) - elif req.method == CardMng.GETREFID: - response = CardMng.getrefid(req) - elif req.method == CardMng.AUTHPASS: - response = CardMng.authpass(req) - elif req.method == CardMng.BINDMODEL: - response = CardMng.bindmodel(req) - elif req.method == CardMng.GETKEEPSPAN: - response = CardMng.getkeepspan() - elif req.method == CardMng.GETDATALIST: - response = CardMng.getdatalist() - else: - raise Exception(f"Not sure how to handle this Cardmng Request: {req}") - elif route == ServiceType.LOCAL.value: - if req.module == Local.SHOPINFO: - response = Local.shopinfo(req) - elif req.module == Local.DEMODATA: - response = Local.demodata(req) - elif req.module == Local.CARDUTIL: - response = Local.cardutil(req) - elif req.module == Local.GAMEINFO: - response = Local.gameinfo(req) - elif req.module == Local.GAMEEND: - response = Local.gameend(req) - elif req.module == Local.GAMETOP: - response = Local.gametop(req) - elif req.module == Local.CUSTOMIZE: - response = Local.customize(req) - else: - raise Exception(f"Not sure how to handle this Local Request: {req}") + if req.method is not None: + method = req.method.title().replace("_", "") + try: + module = importlib.import_module(f"v8_server.eamuse.services.{req.module}") + except ModuleNotFoundError: + app.logger.error(f"No Module found for request: {req}") + raise + + try: + cls = getattr(module, method) + except AttributeError: + app.logger.error(f"No Class found for request: {req}") + raise + + inst = cls(req) + app.logger.debug(inst) + response = inst.response() else: + print(route) raise Exception(f"Not sure how to handle this Request: {req}") + return req.response(response)