Merge branch 'prism_support' into develop

This commit is contained in:
2025-04-08 00:31:31 +00:00
10 changed files with 233 additions and 6 deletions

View File

@ -0,0 +1,50 @@
"""Mai2 Kaleidx Scope Support
Revision ID: 16f34bf7b968
Revises: d0f1c7fa9505
Create Date: 2025-04-02 07:06:15.829591
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '16f34bf7b968'
down_revision = 'd0f1c7fa9505'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('mai2_score_kaleidx_scope',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user', sa.Integer(), nullable=False),
sa.Column('gateId', sa.Integer(), nullable=True),
sa.Column('isGateFound', sa.Boolean(), nullable=True),
sa.Column('isKeyFound', sa.Boolean(), nullable=True),
sa.Column('isClear', sa.Boolean(), nullable=True),
sa.Column('totalRestLife', sa.Integer(), nullable=True),
sa.Column('totalAchievement', sa.Integer(), nullable=True),
sa.Column('totalDeluxscore', sa.Integer(), nullable=True),
sa.Column('bestAchievement', sa.Integer(), nullable=True),
sa.Column('bestDeluxscore', sa.Integer(), nullable=True),
sa.Column('bestAchievementDate', sa.String(length=25), nullable=True),
sa.Column('bestDeluxscoreDate', sa.String(length=25), nullable=True),
sa.Column('playCount', sa.Integer(), nullable=True),
sa.Column('clearDate', sa.String(length=25), nullable=True),
sa.Column('lastPlayDate', sa.String(length=25), nullable=True),
sa.Column('isInfoWatched', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['user'], ['aime_user.id'], onupdate='cascade', ondelete='cascade'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('user', 'gateId', name='mai2_score_best_uk'),
mysql_charset='utf8mb4'
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('mai2_score_kaleidx_scope')
# ### end Alembic commands ###

View File

@ -0,0 +1,28 @@
"""Mai2 add PRiSM support
Revision ID: d0f1c7fa9505
Revises: 1d0014d35220
Create Date: 2025-04-02 06:37:10.657372
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'd0f1c7fa9505'
down_revision = '1d0014d35220'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mai2_playlog', sa.Column('extBool2', sa.Boolean(), nullable=True,server_default=sa.text("NULL")))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mai2_playlog', 'extBool2')
# ### end Alembic commands ###

View File

@ -203,7 +203,7 @@ Presents are items given to the user when they login, with a little animation (f
### Versions
| Game Code | Version ID | Version Name |
| --------- | ---------- | ----------------------- |
|-----------|------------|-------------------------|
| SBXL | 0 | maimai |
| SBXL | 1 | maimai PLUS |
| SBZF | 2 | maimai GreeN |
@ -227,6 +227,8 @@ Presents are items given to the user when they login, with a little animation (f
| SDEZ | 20 | maimai DX FESTiVAL PLUS |
| SDEZ | 21 | maimai DX BUDDiES |
| SDEZ | 22 | maimai DX BUDDiES PLUS |
| SDEZ | 23 | maimai DX PRiSM |
### Importer

View File

@ -52,6 +52,7 @@ Games listed below have been tested and confirmed working. Only game versions ol
+ FESTiVAL PLUS
+ BUDDiES
+ BUDDiES PLUS
+ PRiSM
+ O.N.G.E.K.I.
+ SUMMER

View File

@ -207,7 +207,8 @@ class CardMakerReader(BaseReader):
"1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL,
"1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS,
"1.40": Mai2Constants.VER_MAIMAI_DX_BUDDIES,
"1.45": Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS
"1.45": Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS,
"1.50": Mai2Constants.VER_MAIMAI_DX_PRISM
}
for root, dirs, files in os.walk(base_dir):

View File

@ -59,6 +59,7 @@ class Mai2Constants:
VER_MAIMAI_DX_FESTIVAL_PLUS = 20
VER_MAIMAI_DX_BUDDIES = 21
VER_MAIMAI_DX_BUDDIES_PLUS = 22
VER_MAIMAI_DX_PRISM = 23
VERSION_STRING = (
"maimai",
@ -83,7 +84,8 @@ class Mai2Constants:
"maimai DX FESTiVAL",
"maimai DX FESTiVAL PLUS",
"maimai DX BUDDiES",
"maimai DX BUDDiES PLUS"
"maimai DX BUDDiES PLUS",
"maimai DX PRiSM"
)
MAI_VERSION_LUT = {

View File

@ -112,6 +112,17 @@ class Mai2DX(Mai2Base):
return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"}
# Exp version use this instead of UploadUserPlaylogApi in 1.50
async def handle_upload_user_playlog_list_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
playlog_list = data["userPlaylogList"]
for playlog in playlog_list:
await self.data.score.put_playlog(user_id, playlog)
return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"}
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
charge = data["userCharge"]
@ -271,6 +282,12 @@ class Mai2DX(Mai2Base):
for intimate in upsert["userIntimateList"]:
await self.data.profile.put_intimacy(user_id, intimate["partnerId"], intimate["intimateLevel"], intimate["intimateCountRewarded"])
# added in PRiSM
if "userKaleidxScopeList" in upsert and len(upsert["userKaleidxScopeList"]) > 0:
for kaleidxscope in upsert["userKaleidxScopeList"]:
await self.data.score.put_user_kaleidxscope(user_id, kaleidxscope)
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
async def handle_get_user_data_api_request(self, data: Dict) -> Dict:

View File

@ -31,6 +31,7 @@ from .festival import Mai2Festival
from .festivalplus import Mai2FestivalPlus
from .buddies import Mai2Buddies
from .buddiesplus import Mai2BuddiesPlus
from .prism import Mai2Prism
class Mai2Servlet(BaseServlet):
@ -66,7 +67,8 @@ class Mai2Servlet(BaseServlet):
Mai2Festival,
Mai2FestivalPlus,
Mai2Buddies,
Mai2BuddiesPlus
Mai2BuddiesPlus,
Mai2Prism
]
self.logger = logging.getLogger("mai2")
@ -306,8 +308,11 @@ class Mai2Servlet(BaseServlet):
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
elif version >= 140 and version < 145: # BUDDiES
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES
elif version >= 145: # BUDDiES PLUS
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS
elif version >= 145 and version <150: # BUDDiES PLUS
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS,
elif version >=150:
internal_ver = Mai2Constants.VER_MAIMAI_DX_PRISM
elif game_code == "SDGA": # Int
if version < 105: # 1.0
internal_ver = Mai2Constants.VER_MAIMAI_DX
@ -325,6 +330,12 @@ class Mai2Servlet(BaseServlet):
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
elif version >= 135 and version < 140: # FESTiVAL PLUS
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
elif version >= 140 and version < 145: # BUDDiES
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES
elif version >= 145 and version <150: # BUDDiES PLUS
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS,
elif version >=150:
internal_ver = Mai2Constants.VER_MAIMAI_DX_PRISM
if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32:
# If we get a 32 character long hex string, it's a hash and we're

66
titles/mai2/prism.py Normal file
View File

@ -0,0 +1,66 @@
from typing import Dict
from core.config import CoreConfig
from titles.mai2.buddiesplus import Mai2BuddiesPlus
from titles.mai2.const import Mai2Constants
from titles.mai2.config import Mai2Config
class Mai2Prism(Mai2BuddiesPlus):
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_PRISM
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = await super().handle_cm_get_user_preview_api_request(data)
# hardcode lastDataVersion for CardMaker
user_data["lastDataVersion"] = "1.50.00"
return user_data
async def handle_get_user_new_item_list_api_request(self, data: Dict) -> Dict:
return {
"user_id": data["userId"],
"userItemList": []
}
#seems to be used for downloading music scores online
async def handle_get_game_music_score_api_request(self, data: Dict) -> Dict:
return {
"gameMusicScore": {
"musicId": data["musicId"],
"level": data["level"],
"type": data["type"],
"scoreData": ""
}
}
async def handle_get_game_kaleidx_scope_api_request(self, data: Dict) -> Dict:
return {
"gameKaleidxScopeList": [
{"gateId": 1, "phaseId": 6},
{"gateId": 2, "phaseId": 6},
{"gateId": 3, "phaseId": 6},
{"gateId": 4, "phaseId": 6},
{"gateId": 5, "phaseId": 6},
{"gateId": 6, "phaseId": 6}
]
}
async def handle_get_user_kaleidx_scope_api_request(self, data: Dict) -> Dict:
kaleidxscope = await self.data.score.get_user_kaleidxscope_list(data["userId"])
if kaleidxscope is None:
return {"userId": data["userId"], "userKaleidxScopeList":[]}
kaleidxscope_list = []
for kaleidxscope_data in kaleidxscope:
tmp = kaleidxscope_data._asdict()
tmp.pop("user")
tmp.pop("id")
kaleidxscope_list.append(tmp)
return {
"userId": data["userId"],
"userKaleidxScopeList": kaleidxscope_list
}

View File

@ -1,3 +1,4 @@
from configparser import Interpolation
from typing import Dict, List, Optional
from sqlalchemy import Column, Table, UniqueConstraint, and_
@ -147,6 +148,7 @@ playlog = Table(
Column("extNum2", Integer),
Column("extNum4", Integer),
Column("extBool1", Boolean), # new with buddies
Column("extBool2", Boolean), # new with prism
Column("trialPlayAchievement", Integer),
mysql_charset="utf8mb4",
)
@ -173,6 +175,34 @@ playlog_2p = Table(
mysql_charset="utf8mb4",
)
kaleidxscope = Table(
"mai2_score_kaleidxscope",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("gateId", Integer),
Column("isGateFound", Boolean),
Column("isKeyFound", Boolean),
Column("isClear", Boolean),
Column("totalRestLife", Integer),
Column("totalAchievement", Integer),
Column("totalDeluxscore", Integer),
Column("bestAchievement", Integer),
Column("bestDeluxscore", Integer),
Column("bestAchievementDate", String(25)),
Column("bestDeluxscoreDate", String(25)),
Column("playCount", Integer),
Column("clearDate", String(25)),
Column("lastPlayDate", String(25)),
Column("isInfoWatched", Boolean),
UniqueConstraint("user", "gateId", name="mai2_score_best_uk"),
mysql_charset="utf8mb4"
)
course = Table(
"mai2_score_course",
metadata,
@ -450,3 +480,22 @@ class Mai2ScoreData(BaseData):
self.logger.warning(f"aime_id {aime_id} has no playlog ")
return None
return result.scalar()
async def get_user_kaleidxscope_list(self, user_id: int) -> Optional[List[Row]]:
sql = kaleidxscope.select(kaleidxscope.c.user == user_id)
result = await self.execute(sql)
if result is None:
return None
return result.fetchall()
async def put_user_kaleidxscope(self, user_id: int, user_kaleidxscope_data: Dict) -> Optional[int]:
user_kaleidxscope_data["user"] = user_id
sql = insert(kaleidxscope).values(**user_kaleidxscope_data)
conflict = sql.on_duplicate_key_update(**user_kaleidxscope_data)
result = await self.execute(conflict)
if result is None:
self.logger.error(f"put_user_kaleidxscope: Failed to insert! user_id {user_id}")
return None
return result.lastrowid