Classes for all requests data

- A more generic class loader depending on request url, baed on method
  and module to avoid big if trees. Better separation of data classes as
  well imo.
This commit is contained in:
573dev 2020-11-11 19:59:42 -06:00
parent 186fd3c139
commit a46b0210eb
19 changed files with 1437 additions and 552 deletions

View File

@ -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"]

View File

@ -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.
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<cardmng
cardid="E00401007F7AD7A4"
cardtype="1"
method="inquire"
model="HCV:J:A:A"
update="1"
/>
</call>
"""
# 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<cardid = "{self.cardid}", cardtype = {self.cardtype}, '
f'update = {self.update}, model = "{self.model}">'
)
class Getrefid(object):
"""
Handle the CardMng Getrefid request.
Given the Card ID, password, etc, generate a new RefID for the user and return it
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<cardmng
cardid="E00401007F7AD7A4"
cardtype="1"
method="getrefid"
newflag="0"
passwd="1234"
/>
</call>
"""
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<cardid = "{self.cardid}", cardtype = {self.cardtype}, '
f'newflag = {self.newflag}, passwd = "{self.passwd}">'
)
class Authpass(object):
"""
Handle the CardMng Authpass request.
Given the RefID, and password, verify that the password given is correct for that
user
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<cardmng method="authpass" pass="1234" refid="E9D2DD02072F05C5"/>
</call>
"""
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<passwd = "{self.passwd}", refid = "{self.refid}">'
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
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<cardmng method="bindmodel" newflag="0" refid="A01D0C781F132DBC"/>
</call>
"""
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<refid = "{self.refid}", newflag = {self.newflag}>'
# 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")
'''

View File

@ -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<no = {self.no}, refid = "{self.refid}", uid = "{self.uid}">'
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.
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<cardutil method="check">
<card no="1">
<refid __type="str">E9D2DD02072F05C5</refid>
<uid __type="str">E00401007F7AD7A4</uid>
</card>
</cardutil>
</call>
"""
def __init__(self, req: ServiceRequest) -> None:
self.card = Card(req.xml[0].find("card"))
def __repr__(self) -> str:
return f"Cardutil.Check<card = {self.card}>"
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<no = {self.no}, refid = "{self.refid}", name = "{self.name}", '
f'chara = {self.chara}, uid = "{self.uid}", cabid = {self.cabid}, '
f"is_succession = {self.is_succession}>"
)
class Regist(object):
"""
Handle the Cardutil Regist request.
Register the data for a new user.
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<cardutil method="regist">
<data no="1">
<refid __type="str">E9D2DD02072F05C5</refid>
<name __type="str">AAAA</name>
<chara __type="u8">0</chara>
<uid __type="str">E00401007F7AD7A4</uid>
<cabid __type="u32">1</cabid>
<is_succession __type="s8">0</is_succession>
</data>
</cardutil>
</call>
"""
def __init__(self, req: ServiceRequest) -> None:
self.data = Data(req.xml[0].find("data"))
def __repr__(self) -> str:
return f"Cardutil.Regist<data = {self.data}>"
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")

View File

@ -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<syogo = {self.syogo}>"
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<get = {self.get}>"
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<no = {self.no}, refid = "{self.refid}", '
f"syogodata = {self.syogodata}>"
)
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.
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<customize method="regist">
<player no="1">
<refid __type="str">E9D2DD02072F05C5</refid>
<syogodata>
<get>
<syogo __type="s16">4</syogo>
<syogo __type="s16">0</syogo>
</get>
</syogodata>
</player>
</customize>
</call>
"""
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<players = {self.players}>"
def response(self) -> etree:
return load_xml_template("customize", "regist")

View File

@ -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<locationid = "{self.locationid}">'
class Get(object):
"""
Handle the Demodata Get request.
Return demo data for the hitchart, etc.
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<demodata method="get">
<shop>
<locationid __type="str">CA-123</locationid>
</shop>
<hitchart_nr __type="u16">100</hitchart_nr>
</demodata>
</call>
"""
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<shop = {self.shop}, hitchart_nr = {self.hitchart_nr}>"
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)

View File

@ -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.
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<dlstatus method="progress">
<progress __type="s32">0</progress>
</dlstatus>
</call>
"""
# 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<progress = {self.progress_value}>"

View File

@ -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:
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<facility encoding="SHIFT_JIS" method="get"/>
</call>
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<facility encoding="SHIFT_JIS" method="get"/>
</call>
"""
# 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<encoding = "{self.encoding}">'

View File

@ -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<mode = "{self.mode}">'
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<cabid = {self.cabid}>"
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<musicids = {self.musicids}>"
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<no = {self.no}, musicid = {self.musicid}, "
f"newmusic = {self.newmusic}, longmusic = {self.longmusic}, "
f"kind = {self.kind}>"
)
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<mode = "{self.mode}", stages = {self.stages}, '
f"session = {self.session}>"
)
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<mode = {self.mode}, boss = {self.boss}, "
f"battle_aniv = {self.battle_aniv}, free_music = {self.free_music}, "
f"free_chara = {self.free_chara}, event = {self.event}, "
f"battle_event = {self.battle_event}, champ = {self.champ}, "
f"item = {self.item}, quest = {self.quest}, campaign = {self.campaign}, "
f"gdp = {self.gdp}, v7 = {self.v7}, champ_result = {self.champ_result}>"
)
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<shutter = {self.shutter}, info_level = {self.info_level}, "
f"name_disp = {self.name_disp}, auto = {self.auto}, "
f"random = {self.random}, judge_logo = {self.judge_logo}, "
f"skin = {self.skin}, movie = {self.movie}, "
f"attack_effect = {self.attack_effect}, layout = {self.layout}, "
f"target_skill = {self.target_skill}, comparison = {self.comparison}, "
f"meter_custom = {self.meter_custom}>"
)
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<refid = "{self.refid}", gdp = {self.gdp}, '
f"bad_play_kind = {self.bad_play_kind}, "
f"total_skill_point = {self.total_skill_point}, "
f"styles = {self.styles}, styles_2 = {self.styles_2}, "
f"secret_music = {self.secret_music}, chara = {self.chara}, "
f"secret_chara = {self.secret_chara}, syogo = {self.syogo}, "
f"shutter_list = {self.shutter_list}, "
f"judge_logo_list = {self.judge_logo_list}, "
f"skin_list = {self.skin_list}, movie_list = {self.movie_list}, "
f"attack_effect_list = {self.attack_effect_list}, "
f"idle_screen = {self.idle_screen}, "
f"chance_point = {self.chance_point}, failed_cnt = {self.failed_cnt}, "
f"perfect = {self.perfect}, great = {self.great}, good = {self.good}, "
f"poor = {self.poor}, miss = {self.miss}, time = {self.time}, "
f"exc_clear_num = {self.exc_clear_num}, "
f"full_clear_num = {self.full_clear_num}, "
f"max_clear_difficulty = {self.max_clear_difficulty}, "
f"max_fullcombo_difficulty = {self.max_fullcombo_difficulty}, "
f"max_excellent_difficulty = {self.max_excellent_difficulty}, "
f"champ_group = {self.champ_group}, champ_kind = {self.champ_kind}, "
f"musicid = {self.musicid}, object_musicid = {self.object_musicid}, "
f"seqmode = {self.seqmode}, flags = {self.flags}, "
f"score = {self.score}, point = {self.point}, info = {self.info}, "
f"customize = {self.customize}, bp = {self.bp}, "
f"reserv_item_list = {self.reserv_item_list}, "
f'rival_id_1 = "{self.rival_id_1}", rival_id_2 = "{self.rival_id_2}", '
f'rival_id_3 = "{self.rival_id_3}">'
)
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<no = {self.no}, seqmode = {self.seqmode}, clear = {self.clear}, "
f"auto_clear = {self.auto_clear}, score = {self.score}, "
f"flag = {self.flags}, fullcombo = {self.fullcombo}, "
f"excellent = {self.excellent}, combo = {self.combo}, "
f"skill_point = {self.skill_point}, skill_perc = {self.skill_perc}, "
f"result_rank = {self.result_rank}, difficulty = {self.difficulty}, "
f"combo_rate = {self.combo_rate}, perfect_rate = {self.perfect_rate}>"
)
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<card = "{self.card}", no = {self.no}, '
f"playerinfo = {self.playerinfo}, playdata = {self.playdata}>"
)
class Regist(object):
"""
Handle the Gameend Regist request.
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<gameend method="regist">
<gamemode mode="game_mode"/>
<shop>
<cabid __type="u32">1</cabid>
</shop>
<hitchart>
<musicid __type="s32">1849</musicid>
</hitchart>
<modedata mode="standard">
<stage>
<no __type="u8">1</no>
<musicid __type="s32">1849</musicid>
<newmusic __type="u8">1</newmusic>
<longmusic __type="u8">0</longmusic>
<kind __type="u8">0</kind>
</stage>
<session __type="u8">0</session>
</modedata>
<player card="use" no="1">
<playerinfo>
<refid __type="str">E9D2DD02072F05C5</refid>
<gdp __type="u32">900</gdp>
<bad_play_kind __type="s32">0</bad_play_kind>
<total_skill_point __type="s32">0</total_skill_point>
<styles __type="u32">2097152</styles>
<styles_2 __type="u32">0</styles_2>
<secret_music __type="u16" __count="32">
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
</secret_music>
<chara __type="u8">0</chara>
<secret_chara __type="u32">1824</secret_chara>
<syogo __type="s16" __count="2">0 0</syogo>
<shutter_list __type="u32">0</shutter_list>
<judge_logo_list __type="u32">0</judge_logo_list>
<skin_list __type="u32">0</skin_list>
<movie_list __type="u32">0</movie_list>
<attack_effect_list __type="u32">0</attack_effect_list>
<idle_screen __type="u32">0</idle_screen>
<chance_point __type="s32">0</chance_point>
<failed_cnt __type="s32">0</failed_cnt>
<perfect __type="u32">0</perfect>
<great __type="u32">0</great>
<good __type="u32">0</good>
<poor __type="u32">0</poor>
<miss __type="u32">120</miss>
<time __type="u32">222</time>
<exc_clear_num __type="u32">0</exc_clear_num>
<full_clear_num __type="u32">0</full_clear_num>
<max_clear_difficulty __type="s8">0</max_clear_difficulty>
<max_fullcombo_difficulty __type="s8">0</max_fullcombo_difficulty>
<max_excellent_difficulty __type="s8">0</max_excellent_difficulty>
<champ_group __type="u8">0</champ_group>
<champ_kind __type="u8">0</champ_kind>
<musicid __type="s32" __count="6">0 0 0 0 0 0</musicid>
<object_musicid __type="s32" __count="6">
0 0 0 0 0 0
</object_musicid>
<seqmode __type="s8" __count="6">0 0 0 0 0 0</seqmode>
<flags __type="u32" __count="6">0 0 0 0 0 0</flags>
<score __type="u32" __count="6">0 0 0 0 0 0</score>
<point __type="u32" __count="6">0 0 0 0 0 0</point>
<info>
<mode __type="u32">0</mode>
<boss __type="u32">0</boss>
<battle_aniv __type="u32">0</battle_aniv>
<free_music __type="u32">0</free_music>
<free_chara __type="u32">0</free_chara>
<event __type="u32">0</event>
<battle_event __type="u32">0</battle_event>
<champ __type="u32">0</champ>
<item __type="u32">0</item>
<quest __type="u32">0</quest>
<campaign __type="u32">0</campaign>
<gdp __type="u32">0</gdp>
<v7 __type="u32">0</v7>
<champ_result __type="u32" __count="2">0 0</champ_result>
</info>
<customize>
<shutter __type="u8">0</shutter>
<info_level __type="u8">0</info_level>
<name_disp __type="u8">0</name_disp>
<auto __type="u8">0</auto>
<random __type="u8">0</random>
<judge_logo __type="u32">0</judge_logo>
<skin __type="u32">0</skin>
<movie __type="u32">0</movie>
<attack_effect __type="u32">0</attack_effect>
<layout __type="u8">0</layout>
<target_skill __type="u8">0</target_skill>
<comparison __type="u8">0</comparison>
<meter_custom __type="u8" __count="3">0 0 0</meter_custom>
</customize>
<perfect __type="u32">0</perfect>
<bp __type="u32">0</bp>
<reserv_item_list __type="s16" __count="200">
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
</reserv_item_list>
<rival_id_1 __type="str"></rival_id_1>
<rival_id_2 __type="str"></rival_id_2>
<rival_id_3 __type="str"></rival_id_3>
</playerinfo>
<playdata>
<no __type="u8">1</no>
<seqmode __type="s8">1</seqmode>
<clear __type="u8">0</clear>
<auto_clear __type="u8">0</auto_clear>
<score __type="u32">0</score>
<flags __type="u32">0</flags>
<fullcombo __type="u8">0</fullcombo>
<excellent __type="u8">0</excellent>
<combo __type="u32">0</combo>
<skill_point __type="s32">0</skill_point>
<skill_perc __type="s32">-1</skill_perc>
<result_rank __type="s8">0</result_rank>
<difficulty __type="s8">11</difficulty>
<combo_rate __type="s8">0</combo_rate>
<perfect_rate __type="s8">0</perfect_rate>
</playdata>
</player>
</gameend>
</call>
"""
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<gamemode = {self.gamemode}, shop = {self.shop}, "
f"hitchart = {self.hitchart}, modedata = {self.modedata}, "
f"player = {self.player}>"
)
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)

View File

@ -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<locationid = {self.locationid}, cabid = {self.cabid}>"
class Get(object):
"""
Handle the Gameinfo Get request.
Returns various game data at the start of a game session.
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<gameinfo method="get">
<shop>
<locationid __type="str">CA-123</locationid>
<cabid __type="u32">1</cabid>
</shop>
</gameinfo>
</call>
"""
def __init__(self, req: ServiceRequest) -> None:
self.shop = Shop(req.xml[0].find("shop"))
def __repr__(self) -> str:
return f"Gameinfo.Get<shop = {self.shop}>"
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)

View File

@ -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<kind = {self.kind}, offset = {self.offset}, "
f"music_nr = {self.music_nr}, cabid = {self.cabid}>"
)
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<card = "{self.card}", no = {self.no}, refid = "{self.refid}", '
f"request = {self.request}>"
)
class Get(object):
"""
Handle the Gametop.Get request.
This requests saved user data when using eAmuse.
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<gametop method="get">
<player card="use" no="1">
<refid __type="str">E9D2DD02072F05C5</refid>
<request>
<kind __type="u8">0</kind>
<offset __type="u16">0</offset>
<music_nr __type="u16">250</music_nr>
<cabid __type="u32">1</cabid>
</request>
</player>
</gametop>
</call>
"""
def __init__(self, req: ServiceRequest) -> None:
self.player = Player(req.xml[0].find("player"))
def __repr__(self) -> str:
return f"Gametop.Get<player = {self.player}>"
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
"""

View File

@ -1 +0,0 @@
# >>libshare-pj.c: xrpc_module_add(aLobby, "lobby", aLobby, &off_100404C4);

View File

@ -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

View File

@ -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:
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<message method="get"/>
</call>
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<message method="get"/>
</call>
"""
# 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<>"

View File

@ -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:
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<package method="list" pkgtype="all"/>
</call>
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<package method="list" pkgtype="all"/>
</call>
"""
# 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<pkgtype="{self.pkgtype}">'

View File

@ -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:
<item>
<name __type="str">K32.mode.std</name>
<value __type="s32">1</value>
<time __type="time">1602992363</time>
</item>
<item>
<name __type="str">K32.mode.std</name>
<value __type="s32">1</value>
<time __type="time">1602992363</time>
</item>
"""
DATE_FMT = "%b/%d/%Y-%H:%M:%S"
@ -32,39 +31,28 @@ class PCBEventItem(object):
def __repr__(self) -> str:
return (
f'PCBEventItem<name: "{self.name}", value: {self.value}, '
f'PCBEventPutItem<name: "{self.name}", value: {self.value}, '
f"time: {self.time.strftime(self.DATE_FMT)}>"
)
class PCBEvent(object):
class Put(object):
"""
Handle the PCBEvent request.
It is unknown as to what this does.
Example:
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<pcbevent method="put">
<time __type="time">1602992385</time>
<seq __type="u32">4</seq>
<item>
<name __type="str">K32.mode.std</name>
<value __type="s32">1</value>
<time __type="time">1602992363</time>
</item>
<item>
<name __type="str">K32.playcnt.t</name>
<value __type="s32">1</value>
<time __type="time">1602992363</time>
</item>
<item>
<name __type="str">game.e</name>
<value __type="s32">1</value>
<time __type="time">1602992371</time>
</item>
</pcbevent>
</call>
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<pcbevent method="put">
<time __type="time">1602992385</time>
<seq __type="u32">4</seq>
<item>
<name __type="str">K32.mode.std</name>
<value __type="s32">1</value>
<time __type="time">1602992363</time>
</item>
</pcbevent>
</call>
"""
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:
<response>
<pcbevent expire="600"/>
</response>
"""
# 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<time: {self.time.strftime(self.DATE_FMT)}, "
f"PCBEvent.Put<time: {self.time.strftime(self.DATE_FMT)}, "
f"seq: {self.seq}, items: {self.items}>"
)

View File

@ -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:
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<pcbtracker hardid="010074D435AAD895" method="alive" softid=""/>
</call>
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<pcbtracker hardid="010074D435AAD895" method="alive" softid=""/>
</call>
"""
# 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<hardid="{self.hardid}", softid="{self.softid}", '
f"paseli_active = {self.paseli_active}>"
)

View File

@ -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<volume_bgm = {self.volume_bgm}, "
f"volume_se_myself = {self.volume_se_myself}>"
)
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<difficulty_standard = {self.difficulty_standard}, "
f"max_stage_standard = {self.max_stage_standard}, "
f"long_music = {self.long_music}>"
)
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<game_joining_period = {self.game_joining_period}>"
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<is_shop_close_on = {self.is_shop_close_on}>"
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<stand_alone = {self.stand_alone}, session = {self.session}, "
f"game_settings = {self.game_settings}>"
)
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<coin_slot = {self.coin_slot}>"
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<enable = {self.enable}>"
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<send = {self.send}, sound_options = {self.sound_options}, "
f"game_options = {self.game_options}, coin_options = {self.coin_options}, "
f"bookkeeping = {self.bookkeeping}>"
)
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<name = "{self.name}", pref = {self.pref}, '
f'systemid = "{self.systemid}", softwareid = "{self.softwareid}", '
f'hardwareid = "{self.hardwareid}", locationid = "{self.locationid}", '
f"testmode = {self.testmode}>"
)
class Regist(object):
"""
Handle the Shopinfo Regist request.
Possibly just used to register machine info onto the server.
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
<shopinfo method="regist">
<shop>
<name __type="str"></name>
<pref __type="s8">2</pref>
<systemid __type="str">00010203040506070809</systemid>
<softwareid __type="str">012112345679</softwareid>
<hardwareid __type="str">010074D435AAD895</hardwareid>
<locationid __type="str">CA-123</locationid>
<testmode send="1">
<sound_options>
<volume_bgm __type="u8">20</volume_bgm>
<volume_se_myself __type="u8">20</volume_se_myself>
</sound_options>
<game_options>
<stand_alone>
<difficulty_standard __type="u8">7</difficulty_standard>
<max_stage_standard __type="u8">1</max_stage_standard>
<long_music __type="u8">5</long_music>
</stand_alone>
<session>
<game_joining_period __type="u8">15</game_joining_period>
</session>
<game_settings>
<is_shop_close_on __type="u8">0</is_shop_close_on>
</game_settings>
</game_options>
<coin_options>
<coin_slot __type="u8">1</coin_slot>
</coin_options>
<bookkeeping>
<enable __type="u8">0</enable>
</bookkeeping>
</testmode>
</shop>
</shopinfo>
</call>
"""
def __init__(self, req: ServiceRequest) -> None:
self.shop = Shop(req.xml[0].find("shop"))
def __repr__(self) -> str:
return f"Shopinfo.Regist<shop = {self.shop}>"
def response(self) -> etree:
return load_xml_template("shopinfo", "regist")

View File

@ -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

View File

@ -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)