Compare commits

...

8 Commits

Author SHA1 Message Date
Dniel97 3613f4dbd2
mai2: BUDDiES support added 2024-03-20 21:42:38 +01:00
Dniel97 3cd3910b0d
Merge branch 'develop' into fork_develop 2024-03-20 20:37:32 +01:00
Midorica 942b636b3e cxb: fixing rev s1 support 2024-03-18 22:33:39 -04:00
zaphkito 75bf8f4cb7 add python-multipart to requirements.txt (#112)
because of frontend now required python-multipart in requestments

Reviewed-on: Hay1tsme/artemis#112
Co-authored-by: zaphkito <zaphkito@noreply.gitea.tendokyu.moe>
Co-committed-by: zaphkito <zaphkito@noreply.gitea.tendokyu.moe>
2024-03-14 14:48:06 +00:00
beerpsi 40a0817009 CHUNITHM & O.N.G.E.K.I.: Handle userRatingBase*List (#113)
These tables are not used by the game, but are useful for anyone wanting to develop a web UI showing what the player's rating consists of. As such, instead of storing them in JSON columns, I've split them out, one row per each entry.

Reviewed-on: Hay1tsme/artemis#113
Co-authored-by: beerpsi <beerpsi@duck.com>
Co-committed-by: beerpsi <beerpsi@duck.com>
2024-03-14 14:44:32 +00:00
Hay1tsme 346f82a32a wacca: move allowed_stages into __init__ for s 2024-03-12 14:20:36 -04:00
Hay1tsme f71591e622 wacca: fix S stage up stages 2024-03-12 14:17:26 -04:00
Dniel97 7066651549
Merge branch 'develop' into fork_develop 2024-03-12 18:34:04 +01:00
23 changed files with 373 additions and 112 deletions

View File

@ -1,6 +1,10 @@
# Changelog
Documenting updates to ARTEMiS, to be updated every time the master branch is pushed to.
## 20240318
### CXB
+ Fixing handle_data_shop_list_detail_request for Sunrise S1
## 20240302
### SAO
+ Fixing new profile creation with right heroes and start VP

View File

@ -0,0 +1,56 @@
"""GekiChu rating tables
Revision ID: 6a7e8277763b
Revises: d8950c7ce2fc
Create Date: 2024-03-13 12:18:53.210018
"""
from alembic import op
from sqlalchemy import Column, Integer, String
# revision identifiers, used by Alembic.
revision = '6a7e8277763b'
down_revision = 'd8950c7ce2fc'
branch_labels = None
depends_on = None
GEKICHU_RATING_TABLE_NAMES = [
"chuni_profile_rating",
"ongeki_profile_rating",
]
def upgrade():
for table_name in GEKICHU_RATING_TABLE_NAMES:
op.create_table(
table_name,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", Integer, nullable=False),
Column("version", Integer, nullable=False),
Column("type", String(255), nullable=False),
Column("index", Integer, nullable=False),
Column("musicId", Integer),
Column("difficultId", Integer),
Column("romVersionCode", Integer),
Column("score", Integer),
mysql_charset="utf8mb4",
)
op.create_foreign_key(
None,
table_name,
"aime_user",
["user"],
["id"],
ondelete="cascade",
onupdate="cascade",
)
op.create_unique_constraint(
f"{table_name}_uk",
table_name,
["user", "version", "type", "index"],
)
def downgrade():
for table_name in GEKICHU_RATING_TABLE_NAMES:
op.drop_table(table_name)

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 | 19 | maimai DX FESTiVAL |
| SDEZ | 20 | maimai DX FESTiVAL PLUS |
| SDEZ | 21 | maimai DX BUDDiES |
### Importer
@ -406,6 +407,7 @@ After that, on next login the present should be received (or whenever it suppose
* UNiVERSE PLUS: Yes
* FESTiVAL: Yes (added in A031)
* FESTiVAL PLUS: Yes (added in A035)
* BUDDiES: Yes (added in A039)
* 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
+ FESTiVAL
+ FESTiVAL PLUS
+ BUDDiES
+ O.N.G.E.K.I.
+ SUMMER

View File

@ -1,23 +1,24 @@
mypy
wheel
pytz
pyyaml
sqlalchemy==1.4.46
mysqlclient
pyopenssl
service_identity
PyCryptodome
inflection
coloredlogs
pylibmc; platform_system != "Windows"
wacky
bcrypt
jinja2
protobuf
pillow
pyjwt==2.8.0
websockets
starlette
asyncio
uvicorn
alembic
mypy
wheel
pytz
pyyaml
sqlalchemy==1.4.46
mysqlclient
pyopenssl
service_identity
PyCryptodome
inflection
coloredlogs
pylibmc; platform_system != "Windows"
wacky
bcrypt
jinja2
protobuf
pillow
pyjwt==2.8.0
websockets
starlette
asyncio
uvicorn
alembic
python-multipart

View File

@ -925,6 +925,17 @@ class ChuniBase:
for rp in upsert["userRecentPlayerList"]:
pass
for rating_type in {"userRatingBaseList", "userRatingBaseHotList", "userRatingBaseNextList"}:
if rating_type not in upsert:
continue
await self.data.profile.put_profile_rating(
user_id,
self.version,
rating_type,
upsert[rating_type],
)
return {"returnCode": "1"}
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:

View File

@ -1,10 +1,9 @@
from typing import Dict, List, Optional
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger
from sqlalchemy.engine.base import Connection
from sqlalchemy import Table, Column, UniqueConstraint, and_
from sqlalchemy.types import Integer, String, Boolean, JSON, BigInteger
from sqlalchemy.schema import ForeignKey
from sqlalchemy.engine import Row
from sqlalchemy.sql import func, select
from sqlalchemy.sql import select, delete
from sqlalchemy.dialects.mysql import insert
from core.data.schema import BaseData, metadata
@ -393,6 +392,26 @@ team = Table(
mysql_charset="utf8mb4",
)
rating = Table(
"chuni_profile_rating",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("version", Integer, nullable=False),
Column("type", String(255), nullable=False),
Column("index", Integer, nullable=False),
Column("musicId", Integer),
Column("difficultId", Integer),
Column("romVersionCode", Integer),
Column("score", Integer),
UniqueConstraint("user", "version", "type", "index", name="chuni_profile_rating_best_uk"),
mysql_charset="utf8mb4",
)
class ChuniProfileData(BaseData):
async def update_name(self, user_id: int, new_name: str) -> bool:
@ -714,3 +733,27 @@ class ChuniProfileData(BaseData):
return {
"total_play_count": total_play_count
}
async def put_profile_rating(
self,
aime_id: int,
version: int,
rating_type: str,
rating_data: List[Dict],
):
inserted_values = [
{"user": aime_id, "version": version, "type": rating_type, "index": i, **x}
for (i, x) in enumerate(rating_data)
]
sql = insert(rating).values(inserted_values)
update_dict = {x.name: x for x in sql.inserted if x.name != "id"}
sql = sql.on_duplicate_key_update(**update_dict)
result = await self.execute(sql)
if result is None:
self.logger.warn(
f"put_profile_rating: Could not insert {rating_type}, aime_id: {aime_id}",
)
return
return result.lastrowid

View File

@ -206,6 +206,7 @@ class CardMakerReader(BaseReader):
"1.25": Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS,
"1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL,
"1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS,
"1.40": Mai2Constants.VER_MAIMAI_DX_BUDDIES,
}
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
)
# 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(
version, card_id, name, enabled=enabled
)

View File

@ -1,3 +0,0 @@
saleID.,<2C>J<EFBFBD>n<EFBFBD><6E>,<2C>I<EFBFBD><49><EFBFBD><EFBFBD>,ShopID,Price,
0,1411696799,1443236400,0,7000,
1,1411783199,1443322800,1,7000,
1 saleID. �J�n�� �I���� ShopID Price
2 0 1411696799 1443236400 0 7000
3 1 1411783199 1443322800 1 7000

View File

@ -1,4 +0,0 @@
shopID.,整理用No.,Ver.,出現フラグ,出現フラグ参照ID,条件,出現時間,消滅時間,ItemCode,価格,表示タイプ,Text,Type,Value(op),Value,対象曲,Difficulty(op),Difficulty,Level(op),Level,Grade(Op),Grade,GaugeType(op),GaugeType,HS(op)i,HS,APP,DAP,F-V,F-H,FullCombo,Combo(op),Combo,ClearRate(op),ClearRate,プレイ日,地域,
3000,1,1.00.00,1,-1,-,1411697520.0288,1443233520.0288,skb0000,10,1,MASTER以上の2曲S+以上フルコンボクリアする。,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
3001,2,1.00.00,1,-1,-,1411697520.0288,1443233520.0288,skb0001,10,1,Next Frontier (Masterをクリア,0,-1,-1,bleeze,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
3002,3,1.00.00,1,-1,-,1412103600.0288,1443639598.992,skb0002,10,2,Master以上を1曲をS+以上でクリアする。,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,1,
1 shopID. 整理用No. Ver. 出現フラグ 出現フラグ参照ID 条件 出現時間 消滅時間 ItemCode 価格 表示タイプ Text Type Value(op) Value 対象曲 Difficulty(op) Difficulty Level(op) Level Grade(Op) Grade GaugeType(op) GaugeType HS(op)i HS APP DAP F-V F-H FullCombo Combo(op) Combo ClearRate(op) ClearRate プレイ日 地域
2 3000 1 1.00.00 1 -1 - 1411697520.0288 1443233520.0288 skb0000 10 1 MASTER以上の2曲S+以上フルコンボクリアする。 1 1 1 - 1 2 -1 -1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
3 3001 2 1.00.00 1 -1 - 1411697520.0288 1443233520.0288 skb0001 10 1 Next Frontier (Master)をクリア 0 -1 -1 bleeze 1 2 -1 -1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
4 3002 3 1.00.00 1 -1 - 1412103600.0288 1443639598.992 skb0002 10 2 Master以上を1曲をS+以上でクリアする。 1 1 1 - 1 2 -1 -1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1

View File

@ -1,11 +0,0 @@
shopID.,<2C><><EFBFBD><EFBFBD><EFBFBD>pNo.,Ver.,<2C>o<EFBFBD><6F><EFBFBD>t<EFBFBD><74><EFBFBD>O,<2C>o<EFBFBD><6F><EFBFBD>t<EFBFBD><74><EFBFBD>O<EFBFBD>Q<EFBFBD><51>ID,<2C><><EFBFBD><EFBFBD>,<2C>o<EFBFBD><6F><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD>Ŏ<EFBFBD><C58E><EFBFBD>,ItemCode,<2C><><EFBFBD>i,<2C>\<5C><><EFBFBD>^<5E>C<EFBFBD>v,Text,Type,Value(op),Value,<2C>Ώۋ<CE8F>,Difficulty(op),Difficulty,Level(op),Level,Grade(Op),Grade,GaugeType(op),GaugeType,HS(op)i,HS,APP,DAP,F-V,F-H,FullCombo,Combo(op),Combo,ClearRate(op),ClearRate,<2C>v<EFBFBD><76><EFBFBD>C<EFBFBD><43>,<2C>n<EFBFBD><6E>,
5000,1,10000,1,-1,-,1411697520,1443233520,ske0000,10,1,MASTER<45>ȏ<EFBFBD><C88F>2<EFBFBD><32>S+<2B>ȏ<EFBFBD>t<EFBFBD><74><EFBFBD>R<EFBFBD><52><EFBFBD>{<7B>N<EFBFBD><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
5001,2,10000,1,-1,-,1411697520,1443233520,ske0001,10,1,Next Frontier (Master<65>j<EFBFBD><6A><EFBFBD>N<EFBFBD><4E><EFBFBD>A,0,-1,-1,megaro,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
5002,3,10000,1,-1,-,1412103600,1443639598,ske0002,10,2,Master<65>ȏ<EFBFBD><C88F>1<EFBFBD>Ȃ<EFBFBD>S+<2B>ȏ<EFBFBD>ŃN<C583><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,1,
5003,4,10000,1,-1,-,1412103600,1443639598,ske0003,10,0,Master<65>ȏ<EFBFBD><C88F>1<EFBFBD>Ȃ<EFBFBD>S+<2B>ȏ<EFBFBD>ŃN<C583><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
5004,5,10000,1,-1,-,1412103600,1443639598,ske0004,10,2,2<>ȃN<C883><4E><EFBFBD>A,1,1,2,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
5005,5,10000,1,-1,-,1412103600,1443639598,ske0005,10,2,3<>ȃN<C883><4E><EFBFBD>A,1,1,3,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
5006,5,10000,1,-1,-,1412103600,1443639598,ske0006,10,2,4<>ȃN<C883><4E><EFBFBD>A,1,1,4,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
5007,5,10000,1,-1,-,1412103600,1443639598,ske0007,10,2,5<>ȃN<C883><4E><EFBFBD>A,1,1,5,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
5008,5,10000,1,-1,-,1412103600,1443639598,ske0008,10,2,6<>ȃN<C883><4E><EFBFBD>A,1,1,6,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
5009,5,10000,1,-1,-,1412103600,1443639598,ske0009,10,2,7<>ȃN<C883><4E><EFBFBD>A,1,1,7,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
1 shopID. �����pNo. Ver. �o���t���O �o���t���O�Q��ID ���� �o������ ���Ŏ��� ItemCode ���i �\���^�C�v Text Type Value(op) Value �Ώۋ� Difficulty(op) Difficulty Level(op) Level Grade(Op) Grade GaugeType(op) GaugeType HS(op)i HS APP DAP F-V F-H FullCombo Combo(op) Combo ClearRate(op) ClearRate �v���C�� �n��
2 5000 1 10000 1 -1 - 1411697520 1443233520 ske0000 10 1 MASTER�ȏ��2��S+�ȏ�t���R���{�N���A����B 1 1 1 - 1 2 -1 -1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
3 5001 2 10000 1 -1 - 1411697520 1443233520 ske0001 10 1 Next Frontier (Master�j���N���A 0 -1 -1 megaro -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
4 5002 3 10000 1 -1 - 1412103600 1443639598 ske0002 10 2 Master�ȏ��1�Ȃ�S+�ȏ�ŃN���A����B 1 1 1 - 1 2 -1 -1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1
5 5003 4 10000 1 -1 - 1412103600 1443639598 ske0003 10 0 Master�ȏ��1�Ȃ�S+�ȏ�ŃN���A����B 1 1 1 - 1 2 -1 -1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
6 5004 5 10000 1 -1 - 1412103600 1443639598 ske0004 10 2 2�ȃN���A 1 1 2 - -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
7 5005 5 10000 1 -1 - 1412103600 1443639598 ske0005 10 2 3�ȃN���A 1 1 3 - -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
8 5006 5 10000 1 -1 - 1412103600 1443639598 ske0006 10 2 4�ȃN���A 1 1 4 - -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
9 5007 5 10000 1 -1 - 1412103600 1443639598 ske0007 10 2 5�ȃN���A 1 1 5 - -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
10 5008 5 10000 1 -1 - 1412103600 1443639598 ske0008 10 2 6�ȃN���A 1 1 6 - -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
11 5009 5 10000 1 -1 - 1412103600 1443639598 ske0009 10 2 7�ȃN���A 1 1 7 - -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1

View File

@ -1,6 +0,0 @@
shopID.,<2C><><EFBFBD><EFBFBD><EFBFBD>pNo.,Ver.,<2C>o<EFBFBD><6F><EFBFBD>t<EFBFBD><74><EFBFBD>O,<2C>o<EFBFBD><6F><EFBFBD>t<EFBFBD><74><EFBFBD>O<EFBFBD>Q<EFBFBD><51>ID,<2C><><EFBFBD><EFBFBD>,<2C>o<EFBFBD><6F><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD>Ŏ<EFBFBD><C58E><EFBFBD>,ItemCode,<2C><><EFBFBD>i,<2C>\<5C><><EFBFBD>^<5E>C<EFBFBD>v,Text,Type,Value(op),Value,<2C>Ώۋ<CE8F>,Difficulty(op),Difficulty,Level(op),Level,Grade(Op),Grade,GaugeType(op),GaugeType,HS(op)i,HS,APP,DAP,F-V,F-H,FullCombo,Combo(op),Combo,ClearRate(op),ClearRate,<2C>v<EFBFBD><76><EFBFBD>C<EFBFBD><43>,<2C>n<EFBFBD><6E>,
4000,1,10000,1,-1,-,1411697520,4096483201,skt0000,10,1,MASTER<45>ȏ<EFBFBD><C88F>2<EFBFBD><32>S+<2B>ȏ<EFBFBD>t<EFBFBD><74><EFBFBD>R<EFBFBD><52><EFBFBD>{<7B>N<EFBFBD><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
4001,2,10000,1,-1,-,1411697520,4096483201,skt0001,10,1,Next Frontier (Master<65>j<EFBFBD><6A><EFBFBD>N<EFBFBD><4E><EFBFBD>A,0,-1,-1,megaro,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
4002,3,10000,1,-1,-,1412103600,4096483201,skt0002,10,2,Master<65>ȏ<EFBFBD><C88F>1<EFBFBD>Ȃ<EFBFBD>S+<2B>ȏ<EFBFBD>ŃN<C583><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,1,
4003,4,10000,1,-1,-,1412103600,4096483201,skt0003,10,0,Master<65>ȏ<EFBFBD><C88F>1<EFBFBD>Ȃ<EFBFBD>S+<2B>ȏ<EFBFBD>ŃN<C583><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
4004,5,10000,1,-1,-,1412103600,4096483201,skt0004,10,2,aaaaaaaaaaaaaaaaa,1,1,20,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
1 shopID. �����pNo. Ver. �o���t���O �o���t���O�Q��ID ���� �o������ ���Ŏ��� ItemCode ���i �\���^�C�v Text Type Value(op) Value �Ώۋ� Difficulty(op) Difficulty Level(op) Level Grade(Op) Grade GaugeType(op) GaugeType HS(op)i HS APP DAP F-V F-H FullCombo Combo(op) Combo ClearRate(op) ClearRate �v���C�� �n��
2 4000 1 10000 1 -1 - 1411697520 4096483201 skt0000 10 1 MASTER�ȏ��2��S+�ȏ�t���R���{�N���A����B 1 1 1 - 1 2 -1 -1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
3 4001 2 10000 1 -1 - 1411697520 4096483201 skt0001 10 1 Next Frontier (Master�j���N���A 0 -1 -1 megaro -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
4 4002 3 10000 1 -1 - 1412103600 4096483201 skt0002 10 2 Master�ȏ��1�Ȃ�S+�ȏ�ŃN���A����B 1 1 1 - 1 2 -1 -1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1
5 4003 4 10000 1 -1 - 1412103600 4096483201 skt0003 10 0 Master�ȏ��1�Ȃ�S+�ȏ�ŃN���A����B 1 1 1 - 1 2 -1 -1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
6 4004 5 10000 1 -1 - 1412103600 4096483201 skt0004 10 2 aaaaaaaaaaaaaaaaa 1 1 20 - -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1

View File

@ -73,42 +73,6 @@ class CxbRevSunriseS1(CxbBase):
for line in lines:
ret_str += f"{line[:-1]}\r\n"
# ShopListSale load
ret_str += "\r\n#ShopListSale\r\n"
with open(
r"titles/cxb/data/rss1/Shop/ShopList_Sale.csv", encoding="shift-jis"
) as shop:
lines = shop.readlines()
for line in lines:
ret_str += f"{line[:-1]}\r\n"
# ShopListSkinBg load
ret_str += "\r\n#ShopListSkinBg\r\n"
with open(
r"titles/cxb/data/rss1/Shop/ShopList_SkinBg.csv", encoding="shift-jis"
) as shop:
lines = shop.readlines()
for line in lines:
ret_str += f"{line[:-1]}\r\n"
# ShopListSkinEffect load
ret_str += "\r\n#ShopListSkinEffect\r\n"
with open(
r"titles/cxb/data/rss1/Shop/ShopList_SkinEffect.csv", encoding="shift-jis"
) as shop:
lines = shop.readlines()
for line in lines:
ret_str += f"{line[:-1]}\r\n"
# ShopListSkinNotes load
ret_str += "\r\n#ShopListSkinNotes\r\n"
with open(
r"titles/cxb/data/rss1/Shop/ShopList_SkinNotes.csv", encoding="shift-jis"
) as shop:
lines = shop.readlines()
for line in lines:
ret_str += f"{line[:-1]}\r\n"
# ShopListTitle load
ret_str += "\r\n#ShopListTitle\r\n"
with open(

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_FESTIVAL = 19
VER_MAIMAI_DX_FESTIVAL_PLUS = 20
VER_MAIMAI_DX_BUDDIES = 21
VERSION_STRING = (
"maimai",
@ -76,6 +77,7 @@ class Mai2Constants:
"maimai DX UNiVERSE PLUS",
"maimai DX FESTiVAL",
"maimai DX FESTiVAL PLUS",
"maimai DX BUDDiES"
)
@classmethod

View File

@ -212,6 +212,9 @@ class Mai2DX(Mai2Base):
),
)
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"}

View File

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

View File

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

View File

@ -145,11 +145,34 @@ playlog = Table(
Column("isNewFree", Boolean),
Column("extNum1", Integer),
Column("extNum2", Integer),
Column("extNum4", Integer, server_default="0"),
Column("extNum4", Integer),
Column("extBool1", Boolean), # new with buddies
Column("trialPlayAchievement", Integer),
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(
"mai2_score_course",
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}")
return None
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]:
course_data["user"] = user_id

View File

@ -1067,6 +1067,24 @@ class OngekiBase:
if "userKopList" in upsert:
for x in upsert["userKopList"]:
await self.data.profile.put_kop(user_id, x)
for rating_type in {
"userRatingBaseBestList",
"userRatingBaseBestNewList",
"userRatingBaseHotList",
"userRatingBaseNextList",
"userRatingBaseNextNewList",
"userRatingBaseHotNextList",
}:
if rating_type not in upsert:
continue
await self.data.profile.put_profile_rating(
user_id,
self.version,
rating_type,
upsert[rating_type],
)
return {"returnCode": 1, "apiName": "upsertUserAll"}

View File

@ -246,6 +246,26 @@ rival = Table(
mysql_charset="utf8mb4",
)
rating = Table(
"ongeki_profile_rating",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("version", Integer, nullable=False),
Column("type", String(255), nullable=False),
Column("index", Integer, nullable=False),
Column("musicId", Integer),
Column("difficultId", Integer),
Column("romVersionCode", Integer),
Column("score", Integer),
UniqueConstraint("user", "version", "type", "index", name="ongeki_profile_rating_best_uk"),
mysql_charset="utf8mb4",
)
class OngekiProfileData(BaseData):
def __init__(self, cfg: CoreConfig, conn: Connection) -> None:
@ -508,10 +528,35 @@ class OngekiProfileData(BaseData):
)
return None
return result.lastrowid
async def delete_rival(self, aime_id: int, rival_id: int) -> Optional[int]:
sql = delete(rival).where(rival.c.user==aime_id, rival.c.rivalUserId==rival_id)
result = await self.execute(sql)
if result is None:
self.logger.error(f"delete_rival: failed to delete! aime_id: {aime_id}, rival_id: {rival_id}")
else:
return result.rowcount
return result.rowcount
async def put_profile_rating(
self,
aime_id: int,
version: int,
rating_type: str,
rating_data: List[Dict],
):
inserted_values = [
{"user": aime_id, "version": version, "type": rating_type, "index": i, **x}
for (i, x) in enumerate(rating_data)
]
sql = insert(rating).values(inserted_values)
update_dict = {x.name: x for x in sql.inserted if x.name != "id"}
sql = sql.on_duplicate_key_update(**update_dict)
result = await self.execute(sql)
if result is None:
self.logger.warn(
f"put_profile_rating_{rating_type}: Could not insert rating entries, aime_id: {aime_id}",
)
return
return result.lastrowid

View File

@ -11,25 +11,24 @@ from titles.wacca.handlers import *
class WaccaS(WaccaBase):
allowed_stages = [
(1513, 13),
(1512, 12),
(1511, 11),
(1510, 10),
(1509, 9),
(1508, 8),
(1507, 7),
(1506, 6),
(1505, 5),
(1514, 4),
(1513, 3),
(1512, 2),
(1511, 1),
]
def __init__(self, cfg: CoreConfig, game_cfg: WaccaConfig) -> None:
super().__init__(cfg, game_cfg)
self.version = WaccaConstants.VER_WACCA_S
self.allowed_stages = [
(1513, 13),
(1512, 12),
(1511, 11),
(1510, 10),
(1509, 9),
(1508, 8),
(1507, 7),
(1506, 6),
(1505, 5),
(1504, 4),
(1503, 3),
(1502, 2),
(1501, 1),
]
async def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
resp = GetNewsResponseV2()