forked from Hay1tsme/artemis
Added maimai DX FESTiVAL PLUS support
- Added Card Maker support for FESTiVAL PLUS - Bumped SDEZ database to version 8 - Updated docs for FESTiVAL PLUS
This commit is contained in:
parent
346b74e51b
commit
d6e4db48f4
@ -1,6 +1,13 @@
|
||||
# Changelog
|
||||
Documenting updates to ARTEMiS, to be updated every time the master branch is pushed to.
|
||||
|
||||
## 20231015
|
||||
### maimai DX
|
||||
+ Added support for FESTiVAL PLUS
|
||||
|
||||
### Card Maker
|
||||
+ Added support for maimai DX FESTiVAL PLUS
|
||||
|
||||
## 20230716
|
||||
### General
|
||||
+ Docker files added (#19)
|
||||
|
10
core/data/schema/versions/SDEZ_7_rollback.sql
Normal file
10
core/data/schema/versions/SDEZ_7_rollback.sql
Normal file
@ -0,0 +1,10 @@
|
||||
ALTER TABLE mai2_profile_detail
|
||||
DROP COLUMN mapStock;
|
||||
|
||||
ALTER TABLE mai2_profile_extend
|
||||
DROP COLUMN selectResultScoreViewType;
|
||||
|
||||
ALTER TABLE mai2_profile_option
|
||||
DROP COLUMN outFrameType,
|
||||
DROP COLUMN touchVolume,
|
||||
DROP COLUMN breakSlideVolume;
|
10
core/data/schema/versions/SDEZ_8_upgrade.sql
Normal file
10
core/data/schema/versions/SDEZ_8_upgrade.sql
Normal file
@ -0,0 +1,10 @@
|
||||
ALTER TABLE mai2_profile_detail
|
||||
ADD mapStock INT NULL AFTER playCount;
|
||||
|
||||
ALTER TABLE mai2_profile_extend
|
||||
ADD selectResultScoreViewType INT NULL AFTER selectResultDetails;
|
||||
|
||||
ALTER TABLE mai2_profile_option
|
||||
ADD outFrameType INT NULL AFTER dispCenter,
|
||||
ADD touchVolume INT NULL AFTER slideVolume,
|
||||
ADD breakSlideVolume INT NULL AFTER slideVolume;
|
@ -194,18 +194,19 @@ For versions pre-dx
|
||||
| SDBM | 5 | maimai ORANGE PLUS |
|
||||
| SDCQ | 6 | maimai PiNK |
|
||||
| SDCQ | 7 | maimai PiNK PLUS |
|
||||
| SDDK | 8 | maimai MURASAKI |
|
||||
| SDDK | 9 | maimai MURASAKI PLUS |
|
||||
| SDDZ | 10 | maimai MILK |
|
||||
| SDDZ | 11 | maimai MILK PLUS |
|
||||
| SDDK | 8 | maimai MURASAKi |
|
||||
| SDDK | 9 | maimai MURASAKi PLUS |
|
||||
| SDDZ | 10 | maimai MiLK |
|
||||
| SDDZ | 11 | maimai MiLK PLUS |
|
||||
| SDEY | 12 | maimai FiNALE |
|
||||
| SDEZ | 13 | maimai DX |
|
||||
| SDEZ | 14 | maimai DX PLUS |
|
||||
| SDEZ | 15 | maimai DX Splash |
|
||||
| SDEZ | 16 | maimai DX Splash PLUS |
|
||||
| SDEZ | 17 | maimai DX Universe |
|
||||
| SDEZ | 18 | maimai DX Universe PLUS |
|
||||
| SDEZ | 19 | maimai DX Festival |
|
||||
| SDEZ | 17 | maimai DX UNiVERSE |
|
||||
| SDEZ | 18 | maimai DX UNiVERSE PLUS |
|
||||
| SDEZ | 19 | maimai DX FESTiVAL |
|
||||
| SDEZ | 20 | maimai DX FESTiVAL PLUS |
|
||||
|
||||
### Importer
|
||||
|
||||
@ -347,15 +348,21 @@ python dbutils.py --game SDDT upgrade
|
||||
|
||||
### Support status
|
||||
|
||||
* Card Maker 1.30:
|
||||
* CHUNITHM NEW!!: Yes
|
||||
* maimai DX UNiVERSE: Yes
|
||||
* O.N.G.E.K.I. bright: Yes
|
||||
#### Card Maker 1.30:
|
||||
* CHUNITHM NEW!!: Yes
|
||||
* maimai DX UNiVERSE: Yes
|
||||
* O.N.G.E.K.I. bright: Yes
|
||||
|
||||
* Card Maker 1.35:
|
||||
* CHUNITHM SUN: Yes (NEW PLUS!! up to A032)
|
||||
* maimai DX FESTiVAL: Yes (up to A035) (UNiVERSE PLUS up to A031)
|
||||
* O.N.G.E.K.I. bright MEMORY: Yes
|
||||
#### Card Maker 1.35:
|
||||
* CHUNITHM:
|
||||
* NEW!!: Yes
|
||||
* NEW PLUS!!: Yes (added in A028)
|
||||
* SUN: Yes (added in A032)
|
||||
* maimai DX:
|
||||
* UNiVERSE PLUS: Yes
|
||||
* FESTiVAL: Yes (added in A031)
|
||||
* FESTiVAL PLUS: Yes (added in A035)
|
||||
* O.N.G.E.K.I. bright MEMORY: Yes
|
||||
|
||||
|
||||
### Importer
|
||||
|
@ -11,7 +11,7 @@ Games listed below have been tested and confirmed working. Only game versions ol
|
||||
+ All versions + omnimix
|
||||
|
||||
+ maimai DX
|
||||
+ All versions up to FESTiVAL
|
||||
+ All versions up to FESTiVAL PLUS
|
||||
|
||||
+ Hatsune Miku: Project DIVA Arcade
|
||||
+ All versions
|
||||
|
@ -205,6 +205,7 @@ class CardMakerReader(BaseReader):
|
||||
"1.20": Mai2Constants.VER_MAIMAI_DX_UNIVERSE,
|
||||
"1.25": Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS,
|
||||
"1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL,
|
||||
"1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS,
|
||||
}
|
||||
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
|
@ -16,4 +16,4 @@ game_codes = [
|
||||
Mai2Constants.GAME_CODE_GREEN,
|
||||
Mai2Constants.GAME_CODE,
|
||||
]
|
||||
current_schema_version = 7
|
||||
current_schema_version = 8
|
||||
|
@ -20,13 +20,13 @@ class Mai2Constants:
|
||||
|
||||
DATE_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
GAME_CODE = "SBXL"
|
||||
GAME_CODE_GREEN = "SBZF"
|
||||
GAME_CODE_ORANGE = "SDBM"
|
||||
GAME_CODE_PINK = "SDCQ"
|
||||
GAME_CODE_MURASAKI = "SDDK"
|
||||
GAME_CODE_MILK = "SDDZ"
|
||||
GAME_CODE_FINALE = "SDEY"
|
||||
GAME_CODE = "SBXL"
|
||||
GAME_CODE_GREEN = "SBZF"
|
||||
GAME_CODE_ORANGE = "SDBM"
|
||||
GAME_CODE_PINK = "SDCQ"
|
||||
GAME_CODE_MURASAKI = "SDDK"
|
||||
GAME_CODE_MILK = "SDDZ"
|
||||
GAME_CODE_FINALE = "SDEY"
|
||||
GAME_CODE_DX = "SDEZ"
|
||||
|
||||
CONFIG_NAME = "mai2.yaml"
|
||||
@ -52,6 +52,7 @@ class Mai2Constants:
|
||||
VER_MAIMAI_DX_UNIVERSE = 17
|
||||
VER_MAIMAI_DX_UNIVERSE_PLUS = 18
|
||||
VER_MAIMAI_DX_FESTIVAL = 19
|
||||
VER_MAIMAI_DX_FESTIVAL_PLUS = 20
|
||||
|
||||
VERSION_STRING = (
|
||||
"maimai",
|
||||
@ -66,7 +67,7 @@ class Mai2Constants:
|
||||
"maimai MURASAKi PLUS",
|
||||
"maimai MiLK",
|
||||
"maimai MiLK PLUS",
|
||||
"maimai FiNALE",
|
||||
"maimai FiNALE",
|
||||
"maimai DX",
|
||||
"maimai DX PLUS",
|
||||
"maimai DX Splash",
|
||||
@ -74,6 +75,7 @@ class Mai2Constants:
|
||||
"maimai DX UNiVERSE",
|
||||
"maimai DX UNiVERSE PLUS",
|
||||
"maimai DX FESTiVAL",
|
||||
"maimai DX FESTiVAL PLUS",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
@ -542,8 +542,43 @@ class Mai2DX(Mai2Base):
|
||||
}
|
||||
|
||||
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
class UserRegionList:
|
||||
regionId: int
|
||||
playCount: int
|
||||
created: str
|
||||
"""
|
||||
return {"userId": data["userId"], "length": 0, "userRegionList": []}
|
||||
|
||||
def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
|
||||
user_id = data["userId"]
|
||||
rival_id = data["rivalId"]
|
||||
|
||||
"""
|
||||
class UserRivalData:
|
||||
rivalId: int
|
||||
rivalName: str
|
||||
"""
|
||||
return {"userId": user_id, "userRivalData": {}}
|
||||
|
||||
def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
|
||||
user_id = data["userId"]
|
||||
rival_id = data["rivalId"]
|
||||
next_idx = data["nextIndex"]
|
||||
rival_music_levels = data["userRivalMusicLevelList"]
|
||||
|
||||
"""
|
||||
class UserRivalMusicList:
|
||||
class UserRivalMusicDetailList:
|
||||
level: int
|
||||
achievement: int
|
||||
deluxscoreMax: int
|
||||
|
||||
musicId: int
|
||||
userRivalMusicDetailList: list[UserRivalMusicDetailList]
|
||||
"""
|
||||
return {"userId": user_id, "nextIndex": 0, "userRivalMusicList": []}
|
||||
|
||||
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
|
||||
user_id = data.get("userId", 0)
|
||||
next_index = data.get("nextIndex", 0)
|
||||
|
@ -14,7 +14,7 @@ class Mai2Festival(Mai2UniversePlus):
|
||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
||||
|
||||
# hardcode lastDataVersion for CardMaker 1.35
|
||||
# hardcode lastDataVersion for CardMaker
|
||||
user_data["lastDataVersion"] = "1.30.00"
|
||||
return user_data
|
||||
|
||||
@ -25,7 +25,13 @@ class Mai2Festival(Mai2UniversePlus):
|
||||
return user_login
|
||||
|
||||
def handle_get_user_recommend_rate_music_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
userRecommendRateMusicIdList: list[int]
|
||||
"""
|
||||
return {"userId": data["userId"], "userRecommendRateMusicIdList": []}
|
||||
|
||||
def handle_get_user_recommend_select_music_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
userRecommendSelectionMusicIdList: list[int]
|
||||
"""
|
||||
return {"userId": data["userId"], "userRecommendSelectionMusicIdList": []}
|
||||
|
38
titles/mai2/festivalplus.py
Normal file
38
titles/mai2/festivalplus.py
Normal file
@ -0,0 +1,38 @@
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.mai2.festival import Mai2Festival
|
||||
from titles.mai2.const import Mai2Constants
|
||||
from titles.mai2.config import Mai2Config
|
||||
|
||||
|
||||
class Mai2FestivalPlus(Mai2Festival):
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
|
||||
|
||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
||||
|
||||
# hardcode lastDataVersion for CardMaker
|
||||
user_data["lastDataVersion"] = "1.35.00"
|
||||
return user_data
|
||||
|
||||
def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
|
||||
user_id = data.get("userId", 0)
|
||||
kind = data.get("kind", 2)
|
||||
next_index = data.get("nextIndex", 0)
|
||||
max_ct = data.get("maxCount", 100)
|
||||
is_all = data.get("isAllFavoriteItem", False)
|
||||
|
||||
"""
|
||||
class userFavoriteItemList:
|
||||
orderId: int
|
||||
id: int
|
||||
"""
|
||||
return {
|
||||
"userId": user_id,
|
||||
"kind": kind,
|
||||
"nextIndex": 0,
|
||||
"userFavoriteItemList": [],
|
||||
}
|
@ -23,6 +23,7 @@ from titles.mai2.splashplus import Mai2SplashPlus
|
||||
from titles.mai2.universe import Mai2Universe
|
||||
from titles.mai2.universeplus import Mai2UniversePlus
|
||||
from titles.mai2.festival import Mai2Festival
|
||||
from titles.mai2.festivalplus import Mai2FestivalPlus
|
||||
|
||||
|
||||
class Mai2Servlet:
|
||||
@ -47,7 +48,7 @@ class Mai2Servlet:
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Mai2Finale,
|
||||
Mai2Finale,
|
||||
Mai2DX,
|
||||
Mai2DXPlus,
|
||||
Mai2Splash,
|
||||
@ -55,6 +56,7 @@ class Mai2Servlet:
|
||||
Mai2Universe,
|
||||
Mai2UniversePlus,
|
||||
Mai2Festival,
|
||||
Mai2FestivalPlus,
|
||||
]
|
||||
|
||||
self.logger = logging.getLogger("mai2")
|
||||
@ -110,22 +112,34 @@ class Mai2Servlet:
|
||||
)
|
||||
|
||||
def setup(self):
|
||||
if self.game_cfg.uploads.photos and self.game_cfg.uploads.photos_dir and not path.exists(self.game_cfg.uploads.photos_dir):
|
||||
if (
|
||||
self.game_cfg.uploads.photos
|
||||
and self.game_cfg.uploads.photos_dir
|
||||
and not path.exists(self.game_cfg.uploads.photos_dir)
|
||||
):
|
||||
try:
|
||||
mkdir(self.game_cfg.uploads.photos_dir)
|
||||
except Exception:
|
||||
self.logger.error(f"Failed to make photo upload directory at {self.game_cfg.uploads.photos_dir}")
|
||||
self.logger.error(
|
||||
f"Failed to make photo upload directory at {self.game_cfg.uploads.photos_dir}"
|
||||
)
|
||||
|
||||
if self.game_cfg.uploads.movies and self.game_cfg.uploads.movies_dir and not path.exists(self.game_cfg.uploads.movies_dir):
|
||||
if (
|
||||
self.game_cfg.uploads.movies
|
||||
and self.game_cfg.uploads.movies_dir
|
||||
and not path.exists(self.game_cfg.uploads.movies_dir)
|
||||
):
|
||||
try:
|
||||
mkdir(self.game_cfg.uploads.movies_dir)
|
||||
except Exception:
|
||||
self.logger.error(f"Failed to make movie upload directory at {self.game_cfg.uploads.movies_dir}")
|
||||
self.logger.error(
|
||||
f"Failed to make movie upload directory at {self.game_cfg.uploads.movies_dir}"
|
||||
)
|
||||
|
||||
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
|
||||
if url_path.lower() == "ping":
|
||||
return zlib.compress(b'{"returnCode": "1"}')
|
||||
|
||||
|
||||
elif url_path.startswith("api/movie/"):
|
||||
self.logger.info(f"Movie data: {url_path} - {request.content.getvalue()}")
|
||||
return b""
|
||||
@ -140,18 +154,20 @@ class Mai2Servlet:
|
||||
if request.uri.startswith(b"/SDEZ"):
|
||||
if version < 105: # 1.0
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX
|
||||
elif version >= 105 and version < 110: # Plus
|
||||
elif version >= 105 and version < 110: # PLUS
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_PLUS
|
||||
elif version >= 110 and version < 115: # Splash
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_SPLASH
|
||||
elif version >= 115 and version < 120: # Splash Plus
|
||||
elif version >= 115 and version < 120: # Splash PLUS
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_SPLASH_PLUS
|
||||
elif version >= 120 and version < 125: # Universe
|
||||
elif version >= 120 and version < 125: # UNiVERSE
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE
|
||||
elif version >= 125 and version < 130: # Universe Plus
|
||||
elif version >= 125 and version < 130: # UNiVERSE PLUS
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
|
||||
elif version >= 130: # Festival
|
||||
elif version >= 130 and version < 135: # FESTiVAL
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
|
||||
elif version >= 135: # FESTiVAL PLUS
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
|
||||
|
||||
else:
|
||||
if version < 110: # 1.0
|
||||
@ -180,15 +196,20 @@ class Mai2Servlet:
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_MILK_PLUS
|
||||
elif version >= 197: # Finale
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_FINALE
|
||||
|
||||
if request.getHeader('Mai-Encoding') is not None or request.getHeader('X-Mai-Encoding') is not None:
|
||||
|
||||
if (
|
||||
request.getHeader("Mai-Encoding") is not None
|
||||
or request.getHeader("X-Mai-Encoding") is not None
|
||||
):
|
||||
# The has is some flavor of MD5 of the endpoint with a constant bolted onto the end of it.
|
||||
# See cake.dll's Obfuscator function for details. Hopefully most DLL edits will remove
|
||||
# these two(?) headers to not cause issues, but given the general quality of SEGA data...
|
||||
enc_ver = request.getHeader('Mai-Encoding')
|
||||
enc_ver = request.getHeader("Mai-Encoding")
|
||||
if enc_ver is None:
|
||||
enc_ver = request.getHeader('X-Mai-Encoding')
|
||||
self.logger.debug(f"Encryption v{enc_ver} - User-Agent: {request.getHeader('User-Agent')}")
|
||||
enc_ver = request.getHeader("X-Mai-Encoding")
|
||||
self.logger.debug(
|
||||
f"Encryption v{enc_ver} - User-Agent: {request.getHeader('User-Agent')}"
|
||||
)
|
||||
|
||||
try:
|
||||
unzip = zlib.decompress(req_raw)
|
||||
@ -231,10 +252,12 @@ class Mai2Servlet:
|
||||
self.logger.debug(f"v{version} GET {url_path}")
|
||||
url_split = url_path.split("/")
|
||||
|
||||
if (url_split[0] == "api" and url_split[1] == "movie") or url_split[0] == "movie":
|
||||
if (url_split[0] == "api" and url_split[1] == "movie") or url_split[
|
||||
0
|
||||
] == "movie":
|
||||
if url_split[2] == "moviestart":
|
||||
return json.dumps({"moviestart":{"status":"OK"}}).encode()
|
||||
|
||||
return json.dumps({"moviestart": {"status": "OK"}}).encode()
|
||||
|
||||
else:
|
||||
request.setResponseCode(404)
|
||||
return b""
|
||||
@ -251,20 +274,24 @@ class Mai2Servlet:
|
||||
elif url_split[1].startswith("friend"):
|
||||
self.logger.info(f"v{version} old server friend inquire")
|
||||
return zlib.compress(b"{}")
|
||||
|
||||
|
||||
else:
|
||||
request.setResponseCode(404)
|
||||
return b""
|
||||
|
||||
|
||||
elif url_split[0] == "usbdl":
|
||||
if url_split[1] == "CONNECTIONTEST":
|
||||
self.logger.info(f"v{version} usbdl server test")
|
||||
return b""
|
||||
|
||||
elif self.game_cfg.deliver.udbdl_enable and path.exists(f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}"):
|
||||
with open(f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}", 'rb') as f:
|
||||
|
||||
elif self.game_cfg.deliver.udbdl_enable and path.exists(
|
||||
f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}"
|
||||
):
|
||||
with open(
|
||||
f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}", "rb"
|
||||
) as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
else:
|
||||
request.setResponseCode(404)
|
||||
return b""
|
||||
@ -272,12 +299,18 @@ class Mai2Servlet:
|
||||
elif url_split[0] == "deliver":
|
||||
file = url_split[len(url_split) - 1]
|
||||
self.logger.info(f"v{version} {file} deliver inquire")
|
||||
self.logger.debug(f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}")
|
||||
|
||||
if self.game_cfg.deliver.enable and path.exists(f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}"):
|
||||
with open(f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}", 'rb') as f:
|
||||
self.logger.debug(
|
||||
f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}"
|
||||
)
|
||||
|
||||
if self.game_cfg.deliver.enable and path.exists(
|
||||
f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}"
|
||||
):
|
||||
with open(
|
||||
f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}", "rb"
|
||||
) as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
else:
|
||||
request.setResponseCode(404)
|
||||
return b""
|
||||
|
@ -40,6 +40,7 @@ detail = Table(
|
||||
Column("charaLockSlot", JSON),
|
||||
Column("contentBit", BigInteger),
|
||||
Column("playCount", Integer),
|
||||
Column("mapStock", Integer), # new with fes+
|
||||
Column("eventWatchedDate", String(25)),
|
||||
Column("lastGameId", String(25)),
|
||||
Column("lastRomVersion", String(25)),
|
||||
@ -100,7 +101,7 @@ detail = Table(
|
||||
)
|
||||
|
||||
detail_old = Table(
|
||||
"maimai_profile_detail",
|
||||
"maimai_profile_detail",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column(
|
||||
@ -216,6 +217,7 @@ extend = Table(
|
||||
Column("isPhotoAgree", Boolean),
|
||||
Column("isGotoCodeRead", Boolean),
|
||||
Column("selectResultDetails", Boolean),
|
||||
Column("selectResultScoreViewType", Integer), # new with fes+
|
||||
Column("sortCategorySetting", Integer),
|
||||
Column("sortMusicSetting", Integer),
|
||||
Column("selectedCardList", JSON),
|
||||
@ -251,6 +253,7 @@ option = Table(
|
||||
Column("touchSize", Integer),
|
||||
Column("starRotate", Integer),
|
||||
Column("dispCenter", Integer),
|
||||
Column("outFrameType", Integer), # new with fes+
|
||||
Column("dispChain", Integer),
|
||||
Column("dispRate", Integer),
|
||||
Column("dispBar", Integer),
|
||||
@ -276,6 +279,8 @@ option = Table(
|
||||
Column("exVolume", Integer),
|
||||
Column("slideSe", Integer),
|
||||
Column("slideVolume", Integer),
|
||||
Column("breakSlideVolume", Integer), # new with fes+
|
||||
Column("touchVolume", Integer), # new with fes+
|
||||
Column("touchHoldVolume", Integer),
|
||||
Column("damageSeVolume", Integer),
|
||||
Column("headPhoneVolume", Integer),
|
||||
@ -438,7 +443,11 @@ boss = Table(
|
||||
"maimai_profile_boss",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("pandoraFlagList0", Integer),
|
||||
Column("pandoraFlagList1", Integer),
|
||||
Column("pandoraFlagList2", Integer),
|
||||
@ -455,23 +464,32 @@ recent_rating = Table(
|
||||
"maimai_profile_recent_rating",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("userRecentRatingList", JSON),
|
||||
UniqueConstraint("user", name="mai2_profile_recent_rating_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
consec_logins = Table(
|
||||
"mai2_profile_consec_logins",
|
||||
"mai2_profile_consec_logins",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("logins", Integer),
|
||||
UniqueConstraint("user", "version", name="mai2_profile_consec_logins_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class Mai2ProfileData(BaseData):
|
||||
def put_profile_detail(
|
||||
self, user_id: int, version: int, detail_data: Dict, is_dx: bool = True
|
||||
@ -494,18 +512,22 @@ class Mai2ProfileData(BaseData):
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_profile_detail(self, user_id: int, version: int, is_dx: bool = True) -> Optional[Row]:
|
||||
def get_profile_detail(
|
||||
self, user_id: int, version: int, is_dx: bool = True
|
||||
) -> Optional[Row]:
|
||||
if is_dx:
|
||||
sql = (
|
||||
select(detail)
|
||||
.where(and_(detail.c.user == user_id, detail.c.version <= version))
|
||||
.order_by(detail.c.version.desc())
|
||||
)
|
||||
|
||||
|
||||
else:
|
||||
sql = (
|
||||
select(detail_old)
|
||||
.where(and_(detail_old.c.user == user_id, detail_old.c.version <= version))
|
||||
.where(
|
||||
and_(detail_old.c.user == user_id, detail_old.c.version <= version)
|
||||
)
|
||||
.order_by(detail_old.c.version.desc())
|
||||
)
|
||||
|
||||
@ -582,11 +604,15 @@ class Mai2ProfileData(BaseData):
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warning(f"put_profile_option: failed to update! {user_id} is_dx {is_dx}")
|
||||
self.logger.warning(
|
||||
f"put_profile_option: failed to update! {user_id} is_dx {is_dx}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_profile_option(self, user_id: int, version: int, is_dx: bool = True) -> Optional[Row]:
|
||||
def get_profile_option(
|
||||
self, user_id: int, version: int, is_dx: bool = True
|
||||
) -> Optional[Row]:
|
||||
if is_dx:
|
||||
sql = (
|
||||
select(option)
|
||||
@ -596,7 +622,9 @@ class Mai2ProfileData(BaseData):
|
||||
else:
|
||||
sql = (
|
||||
select(option_old)
|
||||
.where(and_(option_old.c.user == user_id, option_old.c.version <= version))
|
||||
.where(
|
||||
and_(option_old.c.user == user_id, option_old.c.version <= version)
|
||||
)
|
||||
.order_by(option_old.c.version.desc())
|
||||
)
|
||||
|
||||
@ -689,7 +717,9 @@ class Mai2ProfileData(BaseData):
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_web_option(self, user_id: int, version: int, web_opts: Dict) -> Optional[int]:
|
||||
def put_web_option(
|
||||
self, user_id: int, version: int, web_opts: Dict
|
||||
) -> Optional[int]:
|
||||
web_opts["user"] = user_id
|
||||
web_opts["version"] = version
|
||||
sql = insert(web_opt).values(**web_opts)
|
||||
@ -698,14 +728,14 @@ class Mai2ProfileData(BaseData):
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_web_option: failed to update! user_id: {user_id}"
|
||||
)
|
||||
self.logger.warning(f"put_web_option: failed to update! user_id: {user_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_web_option(self, user_id: int, version: int) -> Optional[Row]:
|
||||
sql = web_opt.select(and_(web_opt.c.user == user_id, web_opt.c.version == version))
|
||||
sql = web_opt.select(
|
||||
and_(web_opt.c.user == user_id, web_opt.c.version == version)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
@ -725,7 +755,7 @@ class Mai2ProfileData(BaseData):
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_grade_status(self, user_id: int) -> Optional[Row]:
|
||||
sql = grade_status.select(grade_status.c.user == user_id)
|
||||
|
||||
@ -742,12 +772,10 @@ class Mai2ProfileData(BaseData):
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_boss_list: failed to update! user_id: {user_id}"
|
||||
)
|
||||
self.logger.warning(f"put_boss_list: failed to update! user_id: {user_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_boss_list(self, user_id: int) -> Optional[Row]:
|
||||
sql = boss.select(boss.c.user == user_id)
|
||||
|
||||
@ -759,7 +787,7 @@ class Mai2ProfileData(BaseData):
|
||||
def put_recent_rating(self, user_id: int, rr: Dict) -> Optional[int]:
|
||||
sql = insert(recent_rating).values(user=user_id, userRecentRatingList=rr)
|
||||
|
||||
conflict = sql.on_duplicate_key_update({'userRecentRatingList': rr})
|
||||
conflict = sql.on_duplicate_key_update({"userRecentRatingList": rr})
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
@ -768,7 +796,7 @@ class Mai2ProfileData(BaseData):
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_recent_rating(self, user_id: int) -> Optional[Row]:
|
||||
sql = recent_rating.select(recent_rating.c.user == user_id)
|
||||
|
||||
@ -776,27 +804,25 @@ class Mai2ProfileData(BaseData):
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def add_consec_login(self, user_id: int, version: int) -> None:
|
||||
sql = insert(consec_logins).values(
|
||||
user=user_id,
|
||||
version=version,
|
||||
logins=1
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
logins=consec_logins.c.logins + 1
|
||||
)
|
||||
def add_consec_login(self, user_id: int, version: int) -> None:
|
||||
sql = insert(consec_logins).values(user=user_id, version=version, logins=1)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(logins=consec_logins.c.logins + 1)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.error(f"Failed to update consecutive login count for user {user_id} version {version}")
|
||||
self.logger.error(
|
||||
f"Failed to update consecutive login count for user {user_id} version {version}"
|
||||
)
|
||||
|
||||
def get_consec_login(self, user_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(consec_logins).where(and_(
|
||||
consec_logins.c.user==user_id,
|
||||
consec_logins.c.version==version,
|
||||
))
|
||||
sql = select(consec_logins).where(
|
||||
and_(
|
||||
consec_logins.c.user == user_id,
|
||||
consec_logins.c.version == version,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
@ -804,12 +830,12 @@ class Mai2ProfileData(BaseData):
|
||||
return result.fetchone()
|
||||
|
||||
def reset_consec_login(self, user_id: int, version: int) -> Optional[Row]:
|
||||
sql = consec_logins.update(and_(
|
||||
consec_logins.c.user==user_id,
|
||||
consec_logins.c.version==version,
|
||||
)).values(
|
||||
logins=1
|
||||
)
|
||||
sql = consec_logins.update(
|
||||
and_(
|
||||
consec_logins.c.user == user_id,
|
||||
consec_logins.c.version == version,
|
||||
)
|
||||
).values(logins=1)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
|
@ -23,10 +23,11 @@ class Mai2Universe(Mai2SplashPlus):
|
||||
return {
|
||||
"userName": p["userName"],
|
||||
"rating": p["playerRating"],
|
||||
# hardcode lastDataVersion for CardMaker 1.34
|
||||
# hardcode lastDataVersion for CardMaker
|
||||
"lastDataVersion": "1.20.00",
|
||||
# checks if the user is still logged in
|
||||
"isLogin": False,
|
||||
"isExistSellingCard": False,
|
||||
"isExistSellingCard": True,
|
||||
}
|
||||
|
||||
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||
@ -70,8 +71,12 @@ class Mai2Universe(Mai2SplashPlus):
|
||||
tmp.pop("cardName")
|
||||
tmp.pop("enabled")
|
||||
|
||||
tmp["startDate"] = datetime.strftime(tmp["startDate"], Mai2Constants.DATE_TIME_FORMAT)
|
||||
tmp["endDate"] = datetime.strftime(tmp["endDate"], Mai2Constants.DATE_TIME_FORMAT)
|
||||
tmp["startDate"] = datetime.strftime(
|
||||
tmp["startDate"], Mai2Constants.DATE_TIME_FORMAT
|
||||
)
|
||||
tmp["endDate"] = datetime.strftime(
|
||||
tmp["endDate"], Mai2Constants.DATE_TIME_FORMAT
|
||||
)
|
||||
tmp["noticeStartDate"] = datetime.strftime(
|
||||
tmp["noticeStartDate"], Mai2Constants.DATE_TIME_FORMAT
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user