mirror of
https://github.com/573dev/gfdm-server.git
synced 2026-03-21 17:54:19 -05:00
Have a bunch of stuff working. Next is gametop
This commit is contained in:
parent
27117aa5c4
commit
ca2f46dce6
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -107,3 +107,6 @@ ENV/
|
||||||
/decompiled/
|
/decompiled/
|
||||||
/database/
|
/database/
|
||||||
|
|
||||||
|
|
||||||
|
# Ignore this for now, not sure I want to just throw it up on github yet
|
||||||
|
/v8_server/model/data/mdb.json
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,15 @@ class CardMng(object):
|
||||||
|
|
||||||
if user is None:
|
if user is None:
|
||||||
# The user doesn't exist, force system to create a new account
|
# The user doesn't exist, force system to create a new account
|
||||||
response = E.response(E.cardmng({"status": str(CardMng.NOT_REGISTERED)}))
|
response = E.response(
|
||||||
|
E.cardmng(
|
||||||
|
{
|
||||||
|
"newflag": "1",
|
||||||
|
"binded": "0",
|
||||||
|
"status": str(CardMng.NOT_REGISTERED),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
refid = RefID.from_userid(user.userid)
|
refid = RefID.from_userid(user.userid)
|
||||||
bound = Profile.from_userid(user.userid) is not None
|
bound = Profile.from_userid(user.userid) is not None
|
||||||
|
|
@ -76,12 +84,13 @@ class CardMng(object):
|
||||||
{
|
{
|
||||||
"refid": refid.refid,
|
"refid": refid.refid,
|
||||||
"dataid": refid.refid,
|
"dataid": refid.refid,
|
||||||
"newflag": "1",
|
"newflag": "0",
|
||||||
"binded": "1" if bound else "0",
|
"binded": "1" if bound else "0",
|
||||||
"expired": "0",
|
"expired": "0",
|
||||||
"exflag": "0",
|
"exflag": "0",
|
||||||
"useridflag": "1",
|
"useridflag": "1",
|
||||||
"extidflag": "1",
|
"extidflag": "1",
|
||||||
|
"status": str(CardMng.SUCCESS),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
1
v8_server/eamuse/services/lobby.py
Normal file
1
v8_server/eamuse/services/lobby.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# >>libshare-pj.c: xrpc_module_add(aLobby, "lobby", aLobby, &off_100404C4);
|
||||||
|
|
@ -1,8 +1,18 @@
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from lxml.builder import E
|
from lxml.builder import E
|
||||||
|
|
||||||
|
from v8_server import db
|
||||||
from v8_server.eamuse.services.services import ServiceRequest
|
from v8_server.eamuse.services.services import ServiceRequest
|
||||||
|
from v8_server.eamuse.utils.crc import calculate_crc8
|
||||||
from v8_server.eamuse.utils.xml import XMLBinTypes as T, e_type
|
from v8_server.eamuse.utils.xml import XMLBinTypes as T, e_type
|
||||||
|
from v8_server.model.song import HitChart
|
||||||
|
from v8_server.model.user import User, UserAccount
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Local(object):
|
class Local(object):
|
||||||
|
|
@ -21,10 +31,30 @@ class Local(object):
|
||||||
|
|
||||||
CARDUTIL = "cardutil"
|
CARDUTIL = "cardutil"
|
||||||
CARDUTIL_CHECK = "check"
|
CARDUTIL_CHECK = "check"
|
||||||
|
CARDUTIL_REGIST = "regist"
|
||||||
|
|
||||||
GAMEINFO = "gameinfo"
|
GAMEINFO = "gameinfo"
|
||||||
GAMEINFO_GET = "get"
|
GAMEINFO_GET = "get"
|
||||||
|
|
||||||
|
GAMEEND = "gameend"
|
||||||
|
GAMEEND_REGIST = "regist"
|
||||||
|
|
||||||
|
GAMEINFO = "gameinfo"
|
||||||
|
GAMEINFO_GET = "get"
|
||||||
|
|
||||||
|
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
|
@classmethod
|
||||||
def shopinfo(cls, req: ServiceRequest) -> etree:
|
def shopinfo(cls, req: ServiceRequest) -> etree:
|
||||||
"""
|
"""
|
||||||
|
|
@ -110,6 +140,9 @@ class Local(object):
|
||||||
"""
|
"""
|
||||||
Handle the demodata request.
|
Handle the demodata request.
|
||||||
|
|
||||||
|
Potentially this is just some initial demo data for initial boot/factory reset
|
||||||
|
After this data, the game might keep track of all this stuff itself.
|
||||||
|
|
||||||
# Example Request:
|
# Example Request:
|
||||||
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
|
<call model="K32:J:B:A:2011033000" srcid="00010203040506070809">
|
||||||
<demodata method="get">
|
<demodata method="get">
|
||||||
|
|
@ -125,27 +158,40 @@ class Local(object):
|
||||||
<demodata expire="600"/>
|
<demodata expire="600"/>
|
||||||
</response>
|
</response>
|
||||||
"""
|
"""
|
||||||
# TODO: Figure out what this thing actually needs to send back
|
dtfmt = "%Y-%m-%d %H:%M:%S%z"
|
||||||
|
|
||||||
if req.method == cls.DEMODATA_GET:
|
if req.method == cls.DEMODATA_GET:
|
||||||
# response = E.response(E.demodata({"expire": "600"}))
|
hitchart_number = int(req.xml[0].find("hitchart_nr").text)
|
||||||
|
rank_data = HitChart.get_ranking(hitchart_number)
|
||||||
|
|
||||||
# try some dummy response that might have some info in it
|
|
||||||
response = E.response(
|
response = E.response(
|
||||||
E.demodata(
|
E.demodata(
|
||||||
E.hitchart({"nr": "3"}),
|
E.mode("0", e_type(T.u8)), # Unknown what mode we need
|
||||||
E.data(
|
E.hitchart(
|
||||||
E.musicid("133", e_type(T.s32)),
|
E.start(datetime.now().strftime(dtfmt)),
|
||||||
E.last1("1", e_type(T.s32)),
|
E.end(datetime.now().strftime(dtfmt)),
|
||||||
|
*[
|
||||||
|
E.data(
|
||||||
|
E.musicid(str(x), e_type(T.s32)),
|
||||||
|
E.last1("0", e_type(T.s32)),
|
||||||
|
)
|
||||||
|
for x in rank_data
|
||||||
|
],
|
||||||
|
{"nr": str(hitchart_number)},
|
||||||
),
|
),
|
||||||
E.data(
|
E.bossdata( # No idea what this stuff means
|
||||||
E.musicid("208", e_type(T.s32)),
|
E.division("14", e_type(T.u8)), # Shows up as "Extra Lv X"
|
||||||
E.last1("2", e_type(T.s32)),
|
E.border("0 0 0 0 0 0 0 0 0", e_type(T.u8, count=9)),
|
||||||
),
|
E.extra_border("90", e_type(T.u8)),
|
||||||
E.data(
|
E.bsc_encore_border("92", e_type(T.u8)),
|
||||||
E.musicid("209", e_type(T.s32)),
|
E.adv_encore_border("93", e_type(T.u8)),
|
||||||
E.last1("3", e_type(T.s32)),
|
E.ext_encore_border("94", e_type(T.u8)),
|
||||||
|
E.bsc_premium_border("95", e_type(T.u8)),
|
||||||
|
E.adv_premium_border("95", e_type(T.u8)),
|
||||||
|
E.ext_premium_border("95", e_type(T.u8)),
|
||||||
),
|
),
|
||||||
|
E.info(E.message("SenPi's Kickass Machine")),
|
||||||
|
E.assert_report_state("0", e_type(T.u8)),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
@ -186,6 +232,30 @@ class Local(object):
|
||||||
response = E.response(
|
response = E.response(
|
||||||
E.cardutil(E.card(E.kind("0", e_type(T.s8)), {"no": "1", "state": "0"}))
|
E.cardutil(E.card(E.kind("0", e_type(T.s8)), {"no": "1", "state": "0"}))
|
||||||
)
|
)
|
||||||
|
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 = E.response(E.cardutil())
|
||||||
else:
|
else:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Not sure how to handle this cardutil request. "
|
"Not sure how to handle this cardutil request. "
|
||||||
|
|
@ -217,7 +287,48 @@ class Local(object):
|
||||||
</response>
|
</response>
|
||||||
"""
|
"""
|
||||||
if req.method == cls.GAMEINFO_GET:
|
if req.method == cls.GAMEINFO_GET:
|
||||||
response = E.response(E.gameinfo())
|
response = E.response(
|
||||||
|
E.gameinfo(
|
||||||
|
E.mode("0", e_type(T.u8)),
|
||||||
|
E.free_music("262143", e_type(T.u32)),
|
||||||
|
E.key(E.musicid("-1", e_type(T.s32))),
|
||||||
|
E.limit_gdp("40000", e_type(T.u32)),
|
||||||
|
E.free_chara("1824", e_type(T.u32)),
|
||||||
|
E.tag(str(calculate_crc8(str(262143 + 1824))), e_type(T.u8)),
|
||||||
|
E.bossdata(
|
||||||
|
E.division("14", e_type(T.u8)), # Shows up as "Extra Lv X"
|
||||||
|
E.border("0 0 0 0 0 0 0 0 0", e_type(T.u8, count=9)),
|
||||||
|
E.extra_border("90", e_type(T.u8)),
|
||||||
|
E.bsc_encore_border("92", e_type(T.u8)),
|
||||||
|
E.adv_encore_border("93", e_type(T.u8)),
|
||||||
|
E.ext_encore_border("94", e_type(T.u8)),
|
||||||
|
E.bsc_premium_border("95", e_type(T.u8)),
|
||||||
|
E.adv_premium_border("95", e_type(T.u8)),
|
||||||
|
E.ext_premium_border("95", e_type(T.u8)),
|
||||||
|
),
|
||||||
|
E.battledata(
|
||||||
|
E.battle_music_level(
|
||||||
|
" ".join("0" * 13), e_type(T.u8, count=13)
|
||||||
|
),
|
||||||
|
E.standard_skill(" ".join("0" * 13), e_type(T.s32, count=13)),
|
||||||
|
E.border_skill(" ".join("0" * 13), e_type(T.s32, count=13)),
|
||||||
|
),
|
||||||
|
E.quest(
|
||||||
|
E.division("0", e_type(T.u8)),
|
||||||
|
E.border("0", e_type(T.u8)),
|
||||||
|
E.qdata(" ".join("0" * 26), e_type(T.u32, count=26)),
|
||||||
|
*[
|
||||||
|
E(f"play_{x}", " ".join("0" * 32), e_type(T.u32, count=32))
|
||||||
|
for x in range(0, 13)
|
||||||
|
],
|
||||||
|
*[
|
||||||
|
E(f"clear_{x}", " ".join("0" * 32), e_type(T.u32, count=32))
|
||||||
|
for x in range(0, 13)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
E.campaign(E.campaign("0", e_type(T.u8))),
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Not sure how to handle this gameinfo request. "
|
"Not sure how to handle this gameinfo request. "
|
||||||
|
|
@ -225,3 +336,46 @@ class Local(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def gametop(cls, req: ServiceRequest) -> etree:
|
||||||
|
|
||||||
|
if req.method == cls.GAMETOP_GET:
|
||||||
|
pass
|
||||||
|
elif req.method == cls.GAMETOP_GET_RIVAL:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise Exception(
|
||||||
|
"Not sure how to handle this gametop request. "
|
||||||
|
f'method "{req.method}" is unknown for request: {req}'
|
||||||
|
)
|
||||||
|
|
||||||
|
return request
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def gameend(cls, req: ServiceRequest) -> etree:
|
||||||
|
"""
|
||||||
|
Handle a GameEnd request.
|
||||||
|
|
||||||
|
For now just save the hitchart data
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
response = E.response(E.gameend())
|
||||||
|
else:
|
||||||
|
raise Exception(
|
||||||
|
"Not sure how to handle this gameend request. "
|
||||||
|
f'method "{req.method}" is unknown for request: {req}'
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
|
||||||
|
|
@ -38,20 +38,21 @@ class ServiceType(IntEnum):
|
||||||
|
|
||||||
# Extra for testing
|
# Extra for testing
|
||||||
# BINARY = 8
|
# BINARY = 8
|
||||||
# DLSTATUS = 9
|
DLSTATUS = 9
|
||||||
# EACOIN = 10
|
EACOIN = 10
|
||||||
# EEMALL = 11
|
# EEMALL = 11
|
||||||
# INFO = 12
|
# INFO = 12
|
||||||
# LOBBY = 13
|
LOBBY = 13
|
||||||
# NETLOG = 14
|
NETLOG = 14
|
||||||
# NUMBERING = 15
|
# NUMBERING = 15
|
||||||
# PKGLIST = 16
|
# PKGLIST = 16
|
||||||
# POSEVENT = 17
|
# POSEVENT = 17
|
||||||
# REFERENCE = 18
|
# REFERENCE = 18
|
||||||
# SHOPINF = 19
|
# SHOPINF = 19
|
||||||
# SIDMGR = 20
|
SIDMGR = 20
|
||||||
# USERDATA = 21
|
USERDATA = 21
|
||||||
# USERID = 22
|
USERID = 22
|
||||||
|
TRACEROUTE = 23
|
||||||
|
|
||||||
|
|
||||||
class Services(object):
|
class Services(object):
|
||||||
|
|
@ -66,7 +67,8 @@ class Services(object):
|
||||||
|
|
||||||
# Default service url that GFDM uses. You will need to set up your network so that
|
# Default service url that GFDM uses. You will need to set up your network so that
|
||||||
# this URL points to this server.
|
# this URL points to this server.
|
||||||
SERVICE_URL = "https://eamuse.konami.fun"
|
# SERVICE_URL = "https://eamuse.konami.fun"
|
||||||
|
SERVICE_URL = "https://e.k.f"
|
||||||
|
|
||||||
# The base route that GFDM uses to query the eAmuse server to get the list of
|
# The base route that GFDM uses to query the eAmuse server to get the list of
|
||||||
# offered services
|
# offered services
|
||||||
|
|
|
||||||
14
v8_server/eamuse/utils/crc.py
Normal file
14
v8_server/eamuse/utils/crc.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
def calculate_crc8(value: str) -> int:
|
||||||
|
"""
|
||||||
|
Calculate the CRC8 of a string representation of an int
|
||||||
|
"""
|
||||||
|
crc = 0
|
||||||
|
|
||||||
|
for c in bytearray(value.encode("ASCII")):
|
||||||
|
for i in range(8, 0, -1):
|
||||||
|
t = c ^ crc
|
||||||
|
crc >>= 1
|
||||||
|
if (t & 0x01) != 0:
|
||||||
|
crc ^= 0x8C
|
||||||
|
c >>= 1
|
||||||
|
return crc
|
||||||
103
v8_server/model/song.py
Normal file
103
v8_server/model/song.py
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
|
from flask_sqlalchemy.model import DefaultMeta
|
||||||
|
from sqlalchemy import Column, ForeignKey, PrimaryKeyConstraint, event, func, text
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
from sqlalchemy.types import DateTime, Integer, String
|
||||||
|
|
||||||
|
from v8_server import db
|
||||||
|
|
||||||
|
|
||||||
|
BaseModel: DefaultMeta = db.Model
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Song(BaseModel):
|
||||||
|
"""
|
||||||
|
Table representing the song data
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "songs"
|
||||||
|
musicid = Column(Integer, nullable=False, primary_key=True)
|
||||||
|
bpm = Column(Integer, nullable=False)
|
||||||
|
title_ascii = Column(String(16), nullable=False)
|
||||||
|
hitcharts = relationship("HitChart", back_populates="song")
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
f"Song<musicid: {self.musicid}, bpm: {self.bpm}, "
|
||||||
|
f'title_ascii: "{self.title_ascii}">'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HitChart(BaseModel):
|
||||||
|
"""
|
||||||
|
Table representing the song hit chart
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "hitchart"
|
||||||
|
__table_args__ = (PrimaryKeyConstraint("musicid", "playdate"),)
|
||||||
|
musicid = Column(Integer, ForeignKey("songs.musicid"), nullable=False)
|
||||||
|
playdate = Column(DateTime, nullable=False)
|
||||||
|
song = relationship("Song", back_populates="hitcharts")
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"HitChart<musicid: {self.musicid}, playdate: {self.playdate}>"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_ranking(cls, count) -> List[int]:
|
||||||
|
items = (
|
||||||
|
db.session.query(
|
||||||
|
HitChart.musicid, func.count(HitChart.musicid).label("count")
|
||||||
|
)
|
||||||
|
.group_by(HitChart.musicid)
|
||||||
|
.order_by(text("count DESC"))
|
||||||
|
.order_by(HitChart.musicid.desc())
|
||||||
|
.limit(count)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for item in items:
|
||||||
|
results.append(item[0])
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def insert_initial_song_data(target, connection, **kwargs) -> None:
|
||||||
|
# Make sure both Song and HitChart have been created
|
||||||
|
try:
|
||||||
|
db.session.execute("SELECT * FROM songs")
|
||||||
|
db.session.execute("SELECT * FROM hitchart")
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Load up the mdb.json file
|
||||||
|
data_path = Path(__file__).parent / "data" / "mdb.json"
|
||||||
|
with data_path.open() as f:
|
||||||
|
json_data = json.loads(f.read())
|
||||||
|
|
||||||
|
for key, song in json_data["musicdb"]["songs"].items():
|
||||||
|
song_obj = Song(musicid=key, bpm=song["bpm"], title_ascii=song["title_ascii"])
|
||||||
|
db.session.add(song_obj)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Insert initial hitchart data, just add one entry for every song with the current
|
||||||
|
# timestamp
|
||||||
|
now = datetime.now()
|
||||||
|
for key, song in json_data["musicdb"]["songs"].items():
|
||||||
|
hc = HitChart(musicid=key, playdate=now)
|
||||||
|
db.session.add(hc)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
event.listen(Song.__table__, "after_create", insert_initial_song_data)
|
||||||
|
event.listen(HitChart.__table__, "after_create", insert_initial_song_data)
|
||||||
|
|
@ -6,7 +6,7 @@ from typing import Optional
|
||||||
from flask_sqlalchemy.model import DefaultMeta
|
from flask_sqlalchemy.model import DefaultMeta
|
||||||
from sqlalchemy import JSON, Column, ForeignKey, UniqueConstraint
|
from sqlalchemy import JSON, Column, ForeignKey, UniqueConstraint
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from sqlalchemy.types import Integer, String
|
from sqlalchemy.types import Boolean, Integer, String
|
||||||
|
|
||||||
from v8_server import db
|
from v8_server import db
|
||||||
|
|
||||||
|
|
@ -27,9 +27,10 @@ class User(BaseModel):
|
||||||
|
|
||||||
userid = Column(Integer, nullable=False, primary_key=True)
|
userid = Column(Integer, nullable=False, primary_key=True)
|
||||||
pin = Column(String(4), nullable=False)
|
pin = Column(String(4), nullable=False)
|
||||||
cards = relationship("Card", back_populates="user")
|
card = relationship("Card", uselist=False, back_populates="user")
|
||||||
extids = relationship("ExtID", back_populates="user")
|
extids = relationship("ExtID", back_populates="user")
|
||||||
refids = relationship("RefID", back_populates="user")
|
refids = relationship("RefID", back_populates="user")
|
||||||
|
user_account = relationship("UserAccount", uselist=False, back_populates="user")
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'User<userid: {self.userid}, pin: "{self.pin}">'
|
return f'User<userid: {self.userid}, pin: "{self.pin}">'
|
||||||
|
|
@ -39,6 +40,25 @@ class User(BaseModel):
|
||||||
card = db.session.query(Card).filter(Card.cardid == cardid).one_or_none()
|
card = db.session.query(Card).filter(Card.cardid == cardid).one_or_none()
|
||||||
return card.user if card is not None else None
|
return card.user if card is not None else None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_refid(cls, refid: str) -> Optional[User]:
|
||||||
|
ref = db.session.query(RefID).filter(RefID.refid == refid).one_or_none()
|
||||||
|
return ref.user if ref is not None else None
|
||||||
|
|
||||||
|
|
||||||
|
class UserAccount(BaseModel):
|
||||||
|
"""
|
||||||
|
Table representing a user account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "user_accounts"
|
||||||
|
|
||||||
|
userid = Column(Integer, ForeignKey("users.userid"), primary_key=True)
|
||||||
|
name = Column(String(8), nullable=False)
|
||||||
|
chara = Column(Integer, nullable=False)
|
||||||
|
is_succession = Column(Boolean, nullable=False)
|
||||||
|
user = relationship("User", back_populates="user_account")
|
||||||
|
|
||||||
|
|
||||||
class Card(BaseModel):
|
class Card(BaseModel):
|
||||||
"""
|
"""
|
||||||
|
|
@ -51,7 +71,7 @@ class Card(BaseModel):
|
||||||
|
|
||||||
cardid = Column(String(16), nullable=False, primary_key=True)
|
cardid = Column(String(16), nullable=False, primary_key=True)
|
||||||
userid = Column(Integer, ForeignKey("users.userid"), nullable=False)
|
userid = Column(Integer, ForeignKey("users.userid"), nullable=False)
|
||||||
user = relationship("User", back_populates="cards")
|
user = relationship("User", back_populates="card")
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'Card<cardid: "{self.cardid}", userid: {self.userid}>'
|
return f'Card<cardid: "{self.cardid}", userid: {self.userid}>'
|
||||||
|
|
@ -64,7 +84,7 @@ class ExtID(BaseModel):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__tablename__ = "extids"
|
__tablename__ = "extids"
|
||||||
__table_args_ = (UniqueConstraint("game", "userid", name="game_userid"),)
|
__table_args__ = (UniqueConstraint("game", "userid", name="game_userid"),)
|
||||||
extid = Column(Integer, nullable=False, primary_key=True)
|
extid = Column(Integer, nullable=False, primary_key=True)
|
||||||
game = Column(String(32), nullable=False)
|
game = Column(String(32), nullable=False)
|
||||||
userid = Column(Integer, ForeignKey("users.userid"), nullable=False)
|
userid = Column(Integer, ForeignKey("users.userid"), nullable=False)
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,8 @@ def local_service() -> FlaskResponse:
|
||||||
response = Local.cardutil(req)
|
response = Local.cardutil(req)
|
||||||
elif req.module == Local.GAMEINFO:
|
elif req.module == Local.GAMEINFO:
|
||||||
response = Local.gameinfo(req)
|
response = Local.gameinfo(req)
|
||||||
|
elif req.module == Local.GAMEEND:
|
||||||
|
response = Local.gameend(req)
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Not sure how to handle this Local Request: {req}")
|
raise Exception(f"Not sure how to handle this Local Request: {req}")
|
||||||
return req.response(response)
|
return req.response(response)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user