Compare commits

...

4 Commits

10 changed files with 152 additions and 8 deletions

View File

@ -0,0 +1,68 @@
"""mai2_buddies_support
Revision ID: 81e44dd6047a
Revises: d8950c7ce2fc
Create Date: 2024-03-12 19:10:37.063907
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = "81e44dd6047a"
down_revision = "6a7e8277763b"
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
"mai2_playlog_2p",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user", sa.Integer(), nullable=False),
sa.Column("userId1", sa.Integer(), nullable=True),
sa.Column("userId2", sa.Integer(), nullable=True),
sa.Column("userName1", sa.String(length=25), nullable=True),
sa.Column("userName2", sa.String(length=25), nullable=True),
sa.Column("regionId", sa.Integer(), nullable=True),
sa.Column("placeId", sa.Integer(), nullable=True),
sa.Column("user2pPlaylogDetailList", sa.JSON(), nullable=True),
sa.ForeignKeyConstraint(
["user"], ["aime_user.id"], onupdate="cascade", ondelete="cascade"
),
sa.PrimaryKeyConstraint("id"),
mysql_charset="utf8mb4",
)
op.add_column(
"mai2_playlog",
sa.Column(
"extBool1", sa.Boolean(), nullable=True, server_default=sa.text("NULL")
),
)
op.add_column(
"mai2_profile_detail",
sa.Column(
"renameCredit", sa.Integer(), nullable=True, server_default=sa.text("NULL")
),
)
op.add_column(
"mai2_profile_detail",
sa.Column(
"currentPlayCount",
sa.Integer(),
nullable=True,
server_default=sa.text("NULL"),
),
)
def downgrade():
op.drop_table("mai2_playlog_2p")
op.drop_column("mai2_playlog", "extBool1")
op.drop_column("mai2_profile_detail", "renameCredit")
op.drop_column("mai2_profile_detail", "currentPlayCount")

View File

@ -192,6 +192,7 @@ Config file is located in `config/cxb.yaml`.
| SDEZ | 18 | maimai DX UNiVERSE PLUS | | SDEZ | 18 | maimai DX UNiVERSE PLUS |
| SDEZ | 19 | maimai DX FESTiVAL | | SDEZ | 19 | maimai DX FESTiVAL |
| SDEZ | 20 | maimai DX FESTiVAL PLUS | | SDEZ | 20 | maimai DX FESTiVAL PLUS |
| SDEZ | 21 | maimai DX BUDDiES |
### Importer ### Importer
@ -406,6 +407,7 @@ After that, on next login the present should be received (or whenever it suppose
* UNiVERSE PLUS: Yes * UNiVERSE PLUS: Yes
* FESTiVAL: Yes (added in A031) * FESTiVAL: Yes (added in A031)
* FESTiVAL PLUS: Yes (added in A035) * FESTiVAL PLUS: Yes (added in A035)
* BUDDiES: Yes (added in A039)
* O.N.G.E.K.I. bright MEMORY: Yes * O.N.G.E.K.I. bright MEMORY: Yes

View File

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

View File

@ -206,6 +206,7 @@ class CardMakerReader(BaseReader):
"1.25": Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS, "1.25": Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS,
"1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL, "1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL,
"1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS, "1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS,
"1.40": Mai2Constants.VER_MAIMAI_DX_BUDDIES,
} }
for root, dirs, files in os.walk(base_dir): for root, dirs, files in os.walk(base_dir):
@ -225,12 +226,6 @@ class CardMakerReader(BaseReader):
True if troot.find("disable").text == "false" else False True if troot.find("disable").text == "false" else False
) )
# check if a date is part of the name and disable the
# card if it is
enabled = (
False if re.search(r"\d{2}/\d{2}/\d{2}", name) else enabled
)
await self.mai2_data.static.put_card( await self.mai2_data.static.put_card(
version, card_id, name, enabled=enabled version, card_id, name, enabled=enabled
) )

32
titles/mai2/buddies.py Normal file
View File

@ -0,0 +1,32 @@
from typing import Dict
from core.config import CoreConfig
from titles.mai2.festivalplus import Mai2FestivalPlus
from titles.mai2.const import Mai2Constants
from titles.mai2.config import Mai2Config
class Mai2Buddies(Mai2FestivalPlus):
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_BUDDIES
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.40.00"
return user_data
async def handle_get_user_new_item_api_request(self, data: Dict) -> Dict:
# TODO: Added in 1.41, implement this?
user_id = data["userId"]
version = data.get("version", 1041000)
user_playlog_list = data.get("userPlaylogList", [])
return {
"userId": user_id,
"itemKind": -1,
"itemId": -1,
}

View File

@ -53,6 +53,7 @@ class Mai2Constants:
VER_MAIMAI_DX_UNIVERSE_PLUS = 18 VER_MAIMAI_DX_UNIVERSE_PLUS = 18
VER_MAIMAI_DX_FESTIVAL = 19 VER_MAIMAI_DX_FESTIVAL = 19
VER_MAIMAI_DX_FESTIVAL_PLUS = 20 VER_MAIMAI_DX_FESTIVAL_PLUS = 20
VER_MAIMAI_DX_BUDDIES = 21
VERSION_STRING = ( VERSION_STRING = (
"maimai", "maimai",
@ -76,6 +77,7 @@ class Mai2Constants:
"maimai DX UNiVERSE PLUS", "maimai DX UNiVERSE PLUS",
"maimai DX FESTiVAL", "maimai DX FESTiVAL",
"maimai DX FESTiVAL PLUS", "maimai DX FESTiVAL PLUS",
"maimai DX BUDDiES"
) )
@classmethod @classmethod

View File

@ -212,6 +212,9 @@ class Mai2DX(Mai2Base):
), ),
) )
await self.data.item.put_friend_season_ranking(user_id, fsr) await self.data.item.put_friend_season_ranking(user_id, fsr)
if "user2pPlaylog" in upsert:
await self.data.score.put_playlog_2p(user_id, upsert["user2pPlaylog"])
return {"returnCode": 1, "apiName": "UpsertUserAllApi"} return {"returnCode": 1, "apiName": "UpsertUserAllApi"}

View File

@ -25,6 +25,7 @@ from .universe import Mai2Universe
from .universeplus import Mai2UniversePlus from .universeplus import Mai2UniversePlus
from .festival import Mai2Festival from .festival import Mai2Festival
from .festivalplus import Mai2FestivalPlus from .festivalplus import Mai2FestivalPlus
from .buddies import Mai2Buddies
class Mai2Servlet(BaseServlet): class Mai2Servlet(BaseServlet):
@ -58,6 +59,7 @@ class Mai2Servlet(BaseServlet):
Mai2UniversePlus, Mai2UniversePlus,
Mai2Festival, Mai2Festival,
Mai2FestivalPlus, Mai2FestivalPlus,
Mai2Buddies
] ]
self.logger = logging.getLogger("mai2") self.logger = logging.getLogger("mai2")
@ -257,8 +259,10 @@ class Mai2Servlet(BaseServlet):
internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
elif version >= 130 and version < 135: # FESTiVAL elif version >= 130 and version < 135: # FESTiVAL
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
elif version >= 135: # FESTiVAL PLUS elif version >= 135 and version < 140: # FESTiVAL PLUS
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
elif version >= 140: # BUDDiES
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES
if ( if (
request.headers.get("Mai-Encoding") is not None request.headers.get("Mai-Encoding") is not None

View File

@ -40,6 +40,8 @@ detail = Table(
Column("charaLockSlot", JSON), Column("charaLockSlot", JSON),
Column("contentBit", BigInteger), Column("contentBit", BigInteger),
Column("playCount", Integer), Column("playCount", Integer),
Column("currentPlayCount", Integer), # new with buddies
Column("renameCredit", Integer), # new with buddies
Column("mapStock", Integer), # new with fes+ Column("mapStock", Integer), # new with fes+
Column("eventWatchedDate", String(25)), Column("eventWatchedDate", String(25)),
Column("lastGameId", String(25)), Column("lastGameId", String(25)),

View File

@ -145,11 +145,34 @@ playlog = Table(
Column("isNewFree", Boolean), Column("isNewFree", Boolean),
Column("extNum1", Integer), Column("extNum1", Integer),
Column("extNum2", Integer), Column("extNum2", Integer),
Column("extNum4", Integer, server_default="0"), Column("extNum4", Integer),
Column("extBool1", Boolean), # new with buddies
Column("trialPlayAchievement", Integer), Column("trialPlayAchievement", Integer),
mysql_charset="utf8mb4", mysql_charset="utf8mb4",
) )
# new with buddies
playlog_2p = Table(
"mai2_playlog_2p",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
# TODO: ForeignKey to aime_user?
Column("userId1", Integer),
Column("userId2", Integer),
# TODO: ForeignKey to mai2_profile_detail?
Column("userName1", String(25)),
Column("userName2", String(25)),
Column("regionId", Integer),
Column("placeId", Integer),
Column("user2pPlaylogDetailList", JSON),
mysql_charset="utf8mb4",
)
course = Table( course = Table(
"mai2_score_course", "mai2_score_course",
metadata, metadata,
@ -343,6 +366,18 @@ class Mai2ScoreData(BaseData):
self.logger.error(f"put_playlog: Failed to insert! user_id {user_id} is_dx {is_dx}") self.logger.error(f"put_playlog: Failed to insert! user_id {user_id} is_dx {is_dx}")
return None return None
return result.lastrowid return result.lastrowid
async def put_playlog_2p(self, user_id: int, playlog_2p_data: Dict) -> Optional[int]:
playlog_2p_data["user"] = user_id
sql = insert(playlog_2p).values(**playlog_2p_data)
conflict = sql.on_duplicate_key_update(**playlog_2p_data)
result = await self.execute(conflict)
if result is None:
self.logger.error(f"put_playlog_2p: Failed to insert! user_id {user_id}")
return None
return result.lastrowid
async def put_course(self, user_id: int, course_data: Dict) -> Optional[int]: async def put_course(self, user_id: int, course_data: Dict) -> Optional[int]:
course_data["user"] = user_id course_data["user"] = user_id