forked from Hay1tsme/artemis
Merge pull request 'maimai DX FESTiVAL support' (#17) from Dniel97/artemis:maimai_dx_festival into develop
Reviewed-on: Hay1tsme/artemis#17
This commit is contained in:
commit
017ef1e224
31
core/data/schema/versions/SDEZ_3_rollback.sql
Normal file
31
core/data/schema/versions/SDEZ_3_rollback.sql
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
ALTER TABLE mai2_profile_option
|
||||||
|
DROP COLUMN tapSe;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_score_best
|
||||||
|
DROP COLUMN extNum1;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_profile_extend
|
||||||
|
DROP COLUMN playStatusSetting;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_playlog
|
||||||
|
DROP COLUMN extNum4;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_static_event
|
||||||
|
DROP COLUMN startDate;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_item_map
|
||||||
|
CHANGE COLUMN mapId map_id INT NOT NULL,
|
||||||
|
CHANGE COLUMN isLock is_lock BOOLEAN NOT NULL DEFAULT 0,
|
||||||
|
CHANGE COLUMN isClear is_clear BOOLEAN NOT NULL DEFAULT 0,
|
||||||
|
CHANGE COLUMN isComplete is_complete BOOLEAN NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_item_friend_season_ranking
|
||||||
|
CHANGE COLUMN seasonId season_id INT NOT NULL,
|
||||||
|
CHANGE COLUMN rewardGet reward_get BOOLEAN NOT NULL,
|
||||||
|
CHANGE COLUMN userName user_name VARCHAR(8) NOT NULL,
|
||||||
|
CHANGE COLUMN recordDate record_date VARCHAR(255) NOT NULL;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_item_login_bonus
|
||||||
|
CHANGE COLUMN bonusId bonus_id INT NOT NULL,
|
||||||
|
CHANGE COLUMN isCurrent is_current BOOLEAN NOT NULL DEFAULT 0,
|
||||||
|
CHANGE COLUMN isComplete is_complete BOOLEAN NOT NULL DEFAULT 0;
|
31
core/data/schema/versions/SDEZ_4_upgrade.sql
Normal file
31
core/data/schema/versions/SDEZ_4_upgrade.sql
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
ALTER TABLE mai2_profile_option
|
||||||
|
ADD COLUMN tapSe INT NOT NULL DEFAULT 0 AFTER tapDesign;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_score_best
|
||||||
|
ADD COLUMN extNum1 INT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_profile_extend
|
||||||
|
ADD COLUMN playStatusSetting INT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_playlog
|
||||||
|
ADD COLUMN extNum4 INT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_static_event
|
||||||
|
ADD COLUMN startDate TIMESTAMP NOT NULL DEFAULT current_timestamp();
|
||||||
|
|
||||||
|
ALTER TABLE mai2_item_map
|
||||||
|
CHANGE COLUMN map_id mapId INT NOT NULL,
|
||||||
|
CHANGE COLUMN is_lock isLock BOOLEAN NOT NULL DEFAULT 0,
|
||||||
|
CHANGE COLUMN is_clear isClear BOOLEAN NOT NULL DEFAULT 0,
|
||||||
|
CHANGE COLUMN is_complete isComplete BOOLEAN NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_item_friend_season_ranking
|
||||||
|
CHANGE COLUMN season_id seasonId INT NOT NULL,
|
||||||
|
CHANGE COLUMN reward_get rewardGet BOOLEAN NOT NULL,
|
||||||
|
CHANGE COLUMN user_name userName VARCHAR(8) NOT NULL,
|
||||||
|
CHANGE COLUMN record_date recordDate TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
ALTER TABLE mai2_item_login_bonus
|
||||||
|
CHANGE COLUMN bonus_id bonusId INT NOT NULL,
|
||||||
|
CHANGE COLUMN is_current isCurrent BOOLEAN NOT NULL DEFAULT 0,
|
||||||
|
CHANGE COLUMN is_complete isComplete BOOLEAN NOT NULL DEFAULT 0;
|
@ -113,6 +113,7 @@ Config file is located in `config/cxb.yaml`.
|
|||||||
| 3 | maimai DX Splash PLUS |
|
| 3 | maimai DX Splash PLUS |
|
||||||
| 4 | maimai DX Universe |
|
| 4 | maimai DX Universe |
|
||||||
| 5 | maimai DX Universe PLUS |
|
| 5 | maimai DX Universe PLUS |
|
||||||
|
| 6 | maimai DX Festival |
|
||||||
|
|
||||||
### Importer
|
### Importer
|
||||||
|
|
||||||
@ -125,7 +126,7 @@ python read.py --series SDEZ --version <version ID> --binfolder /path/to/game/fo
|
|||||||
The importer for maimai DX will import Events, Music and Tickets.
|
The importer for maimai DX will import Events, Music and Tickets.
|
||||||
|
|
||||||
**NOTE: It is required to use the importer because the game will
|
**NOTE: It is required to use the importer because the game will
|
||||||
crash without it!**
|
crash without Events!**
|
||||||
|
|
||||||
### Database upgrade
|
### Database upgrade
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ Games listed below have been tested and confirmed working. Only game versions ol
|
|||||||
+ Crossbeats Rev
|
+ Crossbeats Rev
|
||||||
+ All versions + omnimix
|
+ All versions + omnimix
|
||||||
|
|
||||||
+ Maimai
|
+ maimai DX
|
||||||
+ All versions up to Universe Plus
|
+ All versions up to Festival
|
||||||
|
|
||||||
+ Hatsune Miku Arcade
|
+ Hatsune Miku Arcade
|
||||||
+ All versions
|
+ All versions
|
||||||
|
@ -12,6 +12,7 @@ from twisted.web.http import Request
|
|||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
|
from core.utils import Utils
|
||||||
from titles.cm.config import CardMakerConfig
|
from titles.cm.config import CardMakerConfig
|
||||||
from titles.cm.const import CardMakerConstants
|
from titles.cm.const import CardMakerConstants
|
||||||
from titles.cm.base import CardMakerBase
|
from titles.cm.base import CardMakerBase
|
||||||
@ -82,6 +83,7 @@ class CardMakerServlet:
|
|||||||
url_split = url_path.split("/")
|
url_split = url_path.split("/")
|
||||||
internal_ver = 0
|
internal_ver = 0
|
||||||
endpoint = url_split[len(url_split) - 1]
|
endpoint = url_split[len(url_split) - 1]
|
||||||
|
client_ip = Utils.get_ip_addr(request)
|
||||||
|
|
||||||
print(f"version: {version}")
|
print(f"version: {version}")
|
||||||
|
|
||||||
@ -107,7 +109,10 @@ class CardMakerServlet:
|
|||||||
|
|
||||||
req_data = json.loads(unzip)
|
req_data = json.loads(unzip)
|
||||||
|
|
||||||
self.logger.info(f"v{version} {endpoint} request - {req_data}")
|
self.logger.info(
|
||||||
|
f"v{version} {endpoint} request from {client_ip}"
|
||||||
|
)
|
||||||
|
self.logger.debug(req_data)
|
||||||
|
|
||||||
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ class CardMakerReader(BaseReader):
|
|||||||
for dir in data_dirs:
|
for dir in data_dirs:
|
||||||
self.read_chuni_card(f"{dir}/CHU/card")
|
self.read_chuni_card(f"{dir}/CHU/card")
|
||||||
self.read_chuni_gacha(f"{dir}/CHU/gacha")
|
self.read_chuni_gacha(f"{dir}/CHU/gacha")
|
||||||
|
self.read_mai2_card(f"{dir}/MAI/card")
|
||||||
self.read_ongeki_gacha(f"{dir}/MU3/gacha")
|
self.read_ongeki_gacha(f"{dir}/MU3/gacha")
|
||||||
|
|
||||||
def read_chuni_card(self, base_dir: str) -> None:
|
def read_chuni_card(self, base_dir: str) -> None:
|
||||||
@ -206,6 +206,7 @@ class CardMakerReader(BaseReader):
|
|||||||
"1.15": Mai2Constants.VER_MAIMAI_DX_SPLASH_PLUS,
|
"1.15": Mai2Constants.VER_MAIMAI_DX_SPLASH_PLUS,
|
||||||
"1.20": Mai2Constants.VER_MAIMAI_DX_UNIVERSE,
|
"1.20": Mai2Constants.VER_MAIMAI_DX_UNIVERSE,
|
||||||
"1.25": Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS,
|
"1.25": Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS,
|
||||||
|
"1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL,
|
||||||
}
|
}
|
||||||
|
|
||||||
for root, dirs, files in os.walk(base_dir):
|
for root, dirs, files in os.walk(base_dir):
|
||||||
|
@ -7,4 +7,4 @@ index = Mai2Servlet
|
|||||||
database = Mai2Data
|
database = Mai2Data
|
||||||
reader = Mai2Reader
|
reader = Mai2Reader
|
||||||
game_codes = [Mai2Constants.GAME_CODE]
|
game_codes = [Mai2Constants.GAME_CODE]
|
||||||
current_schema_version = 3
|
current_schema_version = 4
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from datetime import datetime, date, timedelta
|
from datetime import datetime, date, timedelta
|
||||||
from typing import Dict
|
from typing import Any, Dict
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
@ -52,6 +52,7 @@ class Mai2Base:
|
|||||||
events = self.data.static.get_enabled_events(self.version)
|
events = self.data.static.get_enabled_events(self.version)
|
||||||
events_lst = []
|
events_lst = []
|
||||||
if events is None:
|
if events is None:
|
||||||
|
self.logger.warn("No enabled events, did you run the reader?")
|
||||||
return {"type": data["type"], "length": 0, "gameEventList": []}
|
return {"type": data["type"], "length": 0, "gameEventList": []}
|
||||||
|
|
||||||
for event in events:
|
for event in events:
|
||||||
@ -59,7 +60,11 @@ class Mai2Base:
|
|||||||
{
|
{
|
||||||
"type": event["type"],
|
"type": event["type"],
|
||||||
"id": event["eventId"],
|
"id": event["eventId"],
|
||||||
"startDate": "2017-12-05 07:00:00.0",
|
# actually use the startDate from the import so it
|
||||||
|
# properly shows all the events when new ones are imported
|
||||||
|
"startDate": datetime.strftime(
|
||||||
|
event["startDate"], f"{Mai2Constants.DATE_TIME_FORMAT}.0"
|
||||||
|
),
|
||||||
"endDate": "2099-12-31 00:00:00.0",
|
"endDate": "2099-12-31 00:00:00.0",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -79,12 +84,12 @@ class Mai2Base:
|
|||||||
return {"length": 0, "gameChargeList": []}
|
return {"length": 0, "gameChargeList": []}
|
||||||
|
|
||||||
charge_list = []
|
charge_list = []
|
||||||
for x in range(len(game_charge_list)):
|
for i, charge in enumerate(game_charge_list):
|
||||||
charge_list.append(
|
charge_list.append(
|
||||||
{
|
{
|
||||||
"orderId": x,
|
"orderId": i,
|
||||||
"chargeId": game_charge_list[x]["ticketId"],
|
"chargeId": charge["ticketId"],
|
||||||
"price": game_charge_list[x]["price"],
|
"price": charge["price"],
|
||||||
"startDate": "2017-12-05 07:00:00.0",
|
"startDate": "2017-12-05 07:00:00.0",
|
||||||
"endDate": "2099-12-31 00:00:00.0",
|
"endDate": "2099-12-31 00:00:00.0",
|
||||||
}
|
}
|
||||||
@ -93,16 +98,16 @@ class Mai2Base:
|
|||||||
return {"length": len(charge_list), "gameChargeList": charge_list}
|
return {"length": len(charge_list), "gameChargeList": charge_list}
|
||||||
|
|
||||||
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
||||||
pass
|
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
||||||
|
|
||||||
def handle_upsert_client_upload_api_request(self, data: Dict) -> Dict:
|
def handle_upsert_client_upload_api_request(self, data: Dict) -> Dict:
|
||||||
pass
|
return {"returnCode": 1, "apiName": "UpsertClientUploadApi"}
|
||||||
|
|
||||||
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||||
pass
|
return {"returnCode": 1, "apiName": "UpsertClientBookkeepingApi"}
|
||||||
|
|
||||||
def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
|
def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
|
||||||
pass
|
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
|
||||||
|
|
||||||
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||||
p = self.data.profile.get_profile_detail(data["userId"], self.version)
|
p = self.data.profile.get_profile_detail(data["userId"], self.version)
|
||||||
@ -167,6 +172,24 @@ class Mai2Base:
|
|||||||
|
|
||||||
self.data.score.put_playlog(user_id, playlog)
|
self.data.score.put_playlog(user_id, playlog)
|
||||||
|
|
||||||
|
return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"}
|
||||||
|
|
||||||
|
def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
|
||||||
|
user_id = data["userId"]
|
||||||
|
charge = data["userCharge"]
|
||||||
|
|
||||||
|
# remove the ".0" from the date string, festival only?
|
||||||
|
charge["purchaseDate"] = charge["purchaseDate"].replace(".0", "")
|
||||||
|
self.data.item.put_charge(
|
||||||
|
user_id,
|
||||||
|
charge["chargeId"],
|
||||||
|
charge["stock"],
|
||||||
|
datetime.strptime(charge["purchaseDate"], Mai2Constants.DATE_TIME_FORMAT),
|
||||||
|
datetime.strptime(charge["validDate"], Mai2Constants.DATE_TIME_FORMAT),
|
||||||
|
)
|
||||||
|
|
||||||
|
return {"returnCode": 1, "apiName": "UpsertUserChargelogApi"}
|
||||||
|
|
||||||
def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
upsert = data["upsertUserAll"]
|
upsert = data["upsertUserAll"]
|
||||||
@ -204,15 +227,21 @@ class Mai2Base:
|
|||||||
|
|
||||||
if "userChargeList" in upsert and len(upsert["userChargeList"]) > 0:
|
if "userChargeList" in upsert and len(upsert["userChargeList"]) > 0:
|
||||||
for charge in upsert["userChargeList"]:
|
for charge in upsert["userChargeList"]:
|
||||||
|
# remove the ".0" from the date string, festival only?
|
||||||
|
charge["purchaseDate"] = charge["purchaseDate"].replace(".0", "")
|
||||||
self.data.item.put_charge(
|
self.data.item.put_charge(
|
||||||
user_id,
|
user_id,
|
||||||
charge["chargeId"],
|
charge["chargeId"],
|
||||||
charge["stock"],
|
charge["stock"],
|
||||||
datetime.strptime(charge["purchaseDate"], "%Y-%m-%d %H:%M:%S"),
|
datetime.strptime(
|
||||||
datetime.strptime(charge["validDate"], "%Y-%m-%d %H:%M:%S")
|
charge["purchaseDate"], Mai2Constants.DATE_TIME_FORMAT
|
||||||
|
),
|
||||||
|
datetime.strptime(
|
||||||
|
charge["validDate"], Mai2Constants.DATE_TIME_FORMAT
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if upsert["isNewCharacterList"] and int(upsert["isNewCharacterList"]) > 0:
|
if "userCharacterList" in upsert and len(upsert["userCharacterList"]) > 0:
|
||||||
for char in upsert["userCharacterList"]:
|
for char in upsert["userCharacterList"]:
|
||||||
self.data.item.put_character(
|
self.data.item.put_character(
|
||||||
user_id,
|
user_id,
|
||||||
@ -222,7 +251,7 @@ class Mai2Base:
|
|||||||
char["useCount"],
|
char["useCount"],
|
||||||
)
|
)
|
||||||
|
|
||||||
if upsert["isNewItemList"] and int(upsert["isNewItemList"]) > 0:
|
if "userItemList" in upsert and len(upsert["userItemList"]) > 0:
|
||||||
for item in upsert["userItemList"]:
|
for item in upsert["userItemList"]:
|
||||||
self.data.item.put_item(
|
self.data.item.put_item(
|
||||||
user_id,
|
user_id,
|
||||||
@ -232,7 +261,7 @@ class Mai2Base:
|
|||||||
item["isValid"],
|
item["isValid"],
|
||||||
)
|
)
|
||||||
|
|
||||||
if upsert["isNewLoginBonusList"] and int(upsert["isNewLoginBonusList"]) > 0:
|
if "userLoginBonusList" in upsert and len(upsert["userLoginBonusList"]) > 0:
|
||||||
for login_bonus in upsert["userLoginBonusList"]:
|
for login_bonus in upsert["userLoginBonusList"]:
|
||||||
self.data.item.put_login_bonus(
|
self.data.item.put_login_bonus(
|
||||||
user_id,
|
user_id,
|
||||||
@ -242,7 +271,7 @@ class Mai2Base:
|
|||||||
login_bonus["isComplete"],
|
login_bonus["isComplete"],
|
||||||
)
|
)
|
||||||
|
|
||||||
if upsert["isNewMapList"] and int(upsert["isNewMapList"]) > 0:
|
if "userMapList" in upsert and len(upsert["userMapList"]) > 0:
|
||||||
for map in upsert["userMapList"]:
|
for map in upsert["userMapList"]:
|
||||||
self.data.item.put_map(
|
self.data.item.put_map(
|
||||||
user_id,
|
user_id,
|
||||||
@ -253,24 +282,32 @@ class Mai2Base:
|
|||||||
map["isComplete"],
|
map["isComplete"],
|
||||||
)
|
)
|
||||||
|
|
||||||
if upsert["isNewMusicDetailList"] and int(upsert["isNewMusicDetailList"]) > 0:
|
if "userMusicDetailList" in upsert and len(upsert["userMusicDetailList"]) > 0:
|
||||||
for music in upsert["userMusicDetailList"]:
|
for music in upsert["userMusicDetailList"]:
|
||||||
self.data.score.put_best_score(user_id, music)
|
self.data.score.put_best_score(user_id, music)
|
||||||
|
|
||||||
if upsert["isNewCourseList"] and int(upsert["isNewCourseList"]) > 0:
|
if "userCourseList" in upsert and len(upsert["userCourseList"]) > 0:
|
||||||
for course in upsert["userCourseList"]:
|
for course in upsert["userCourseList"]:
|
||||||
self.data.score.put_course(user_id, course)
|
self.data.score.put_course(user_id, course)
|
||||||
|
|
||||||
if upsert["isNewFavoriteList"] and int(upsert["isNewFavoriteList"]) > 0:
|
if "userFavoriteList" in upsert and len(upsert["userFavoriteList"]) > 0:
|
||||||
for fav in upsert["userFavoriteList"]:
|
for fav in upsert["userFavoriteList"]:
|
||||||
self.data.item.put_favorite(user_id, fav["kind"], fav["itemIdList"])
|
self.data.item.put_favorite(user_id, fav["kind"], fav["itemIdList"])
|
||||||
|
|
||||||
# if "isNewFriendSeasonRankingList" in upsert and int(upsert["isNewFriendSeasonRankingList"]) > 0:
|
if (
|
||||||
# for fsr in upsert["userFriendSeasonRankingList"]:
|
"userFriendSeasonRankingList" in upsert
|
||||||
# pass
|
and len(upsert["userFriendSeasonRankingList"]) > 0
|
||||||
|
):
|
||||||
|
for fsr in upsert["userFriendSeasonRankingList"]:
|
||||||
|
fsr["recordDate"] = (
|
||||||
|
datetime.strptime(fsr["recordDate"], f"{Mai2Constants.DATE_TIME_FORMAT}.0"),
|
||||||
|
)
|
||||||
|
self.data.item.put_friend_season_ranking(user_id, fsr)
|
||||||
|
|
||||||
|
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
|
||||||
|
|
||||||
def handle_user_logout_api_request(self, data: Dict) -> Dict:
|
def handle_user_logout_api_request(self, data: Dict) -> Dict:
|
||||||
pass
|
return {"returnCode": 1}
|
||||||
|
|
||||||
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
|
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
|
||||||
@ -311,11 +348,7 @@ class Mai2Base:
|
|||||||
def handle_get_user_card_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_card_api_request(self, data: Dict) -> Dict:
|
||||||
user_cards = self.data.item.get_cards(data["userId"])
|
user_cards = self.data.item.get_cards(data["userId"])
|
||||||
if user_cards is None:
|
if user_cards is None:
|
||||||
return {
|
return {"userId": data["userId"], "nextIndex": 0, "userCardList": []}
|
||||||
"userId": data["userId"],
|
|
||||||
"nextIndex": 0,
|
|
||||||
"userCardList": []
|
|
||||||
}
|
|
||||||
|
|
||||||
max_ct = data["maxCount"]
|
max_ct = data["maxCount"]
|
||||||
next_idx = data["nextIndex"]
|
next_idx = data["nextIndex"]
|
||||||
@ -333,25 +366,23 @@ class Mai2Base:
|
|||||||
tmp.pop("id")
|
tmp.pop("id")
|
||||||
tmp.pop("user")
|
tmp.pop("user")
|
||||||
tmp["startDate"] = datetime.strftime(
|
tmp["startDate"] = datetime.strftime(
|
||||||
tmp["startDate"], "%Y-%m-%d %H:%M:%S")
|
tmp["startDate"], Mai2Constants.DATE_TIME_FORMAT
|
||||||
|
)
|
||||||
tmp["endDate"] = datetime.strftime(
|
tmp["endDate"] = datetime.strftime(
|
||||||
tmp["endDate"], "%Y-%m-%d %H:%M:%S")
|
tmp["endDate"], Mai2Constants.DATE_TIME_FORMAT
|
||||||
|
)
|
||||||
card_list.append(tmp)
|
card_list.append(tmp)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"nextIndex": next_idx,
|
"nextIndex": next_idx,
|
||||||
"userCardList": card_list[start_idx:end_idx]
|
"userCardList": card_list[start_idx:end_idx],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
|
||||||
user_charges = self.data.item.get_charges(data["userId"])
|
user_charges = self.data.item.get_charges(data["userId"])
|
||||||
if user_charges is None:
|
if user_charges is None:
|
||||||
return {
|
return {"userId": data["userId"], "length": 0, "userChargeList": []}
|
||||||
"userId": data["userId"],
|
|
||||||
"length": 0,
|
|
||||||
"userChargeList": []
|
|
||||||
}
|
|
||||||
|
|
||||||
user_charge_list = []
|
user_charge_list = []
|
||||||
for charge in user_charges:
|
for charge in user_charges:
|
||||||
@ -359,45 +390,46 @@ class Mai2Base:
|
|||||||
tmp.pop("id")
|
tmp.pop("id")
|
||||||
tmp.pop("user")
|
tmp.pop("user")
|
||||||
tmp["purchaseDate"] = datetime.strftime(
|
tmp["purchaseDate"] = datetime.strftime(
|
||||||
tmp["purchaseDate"], "%Y-%m-%d %H:%M:%S")
|
tmp["purchaseDate"], Mai2Constants.DATE_TIME_FORMAT
|
||||||
|
)
|
||||||
tmp["validDate"] = datetime.strftime(
|
tmp["validDate"] = datetime.strftime(
|
||||||
tmp["validDate"], "%Y-%m-%d %H:%M:%S")
|
tmp["validDate"], Mai2Constants.DATE_TIME_FORMAT
|
||||||
|
)
|
||||||
|
|
||||||
user_charge_list.append(tmp)
|
user_charge_list.append(tmp)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"length": len(user_charge_list),
|
"length": len(user_charge_list),
|
||||||
"userChargeList": user_charge_list
|
"userChargeList": user_charge_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||||
kind = int(data["nextIndex"] / 10000000000)
|
kind = int(data["nextIndex"] / 10000000000)
|
||||||
next_idx = int(data["nextIndex"] % 10000000000)
|
next_idx = int(data["nextIndex"] % 10000000000)
|
||||||
user_items = self.data.item.get_items(data["userId"], kind)
|
user_item_list = self.data.item.get_items(data["userId"], kind)
|
||||||
user_item_list = []
|
|
||||||
|
items: list[Dict[str, Any]] = []
|
||||||
|
for i in range(next_idx, len(user_item_list)):
|
||||||
|
tmp = user_item_list[i]._asdict()
|
||||||
|
tmp.pop("user")
|
||||||
|
tmp.pop("id")
|
||||||
|
items.append(tmp)
|
||||||
|
if len(items) >= int(data["maxCount"]):
|
||||||
|
break
|
||||||
|
|
||||||
|
xout = kind * 10000000000 + next_idx + len(items)
|
||||||
|
|
||||||
|
if len(items) < int(data["maxCount"]):
|
||||||
next_idx = 0
|
next_idx = 0
|
||||||
|
else:
|
||||||
for x in range(next_idx, data["maxCount"]):
|
next_idx = xout
|
||||||
try:
|
|
||||||
user_item_list.append({
|
|
||||||
"itemKind": user_items[x]["itemKind"],
|
|
||||||
"itemId": user_items[x]["itemId"],
|
|
||||||
"stock": user_items[x]["stock"],
|
|
||||||
"isValid": user_items[x]["isValid"]
|
|
||||||
})
|
|
||||||
except IndexError:
|
|
||||||
break
|
|
||||||
|
|
||||||
if len(user_item_list) == data["maxCount"]:
|
|
||||||
next_idx = data["nextIndex"] + data["maxCount"] + 1
|
|
||||||
break
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"nextIndex": next_idx,
|
"nextIndex": next_idx,
|
||||||
"itemKind": kind,
|
"itemKind": kind,
|
||||||
"userItemList": user_item_list
|
"userItemList": items,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
|
||||||
@ -479,21 +511,12 @@ class Mai2Base:
|
|||||||
tmp.pop("user")
|
tmp.pop("user")
|
||||||
mlst.append(tmp)
|
mlst.append(tmp)
|
||||||
|
|
||||||
return {
|
return {"userActivity": {"playList": plst, "musicList": mlst}}
|
||||||
"userActivity": {
|
|
||||||
"playList": plst,
|
|
||||||
"musicList": mlst
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def handle_get_user_course_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_course_api_request(self, data: Dict) -> Dict:
|
||||||
user_courses = self.data.score.get_courses(data["userId"])
|
user_courses = self.data.score.get_courses(data["userId"])
|
||||||
if user_courses is None:
|
if user_courses is None:
|
||||||
return {
|
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []}
|
||||||
"userId": data["userId"],
|
|
||||||
"nextIndex": 0,
|
|
||||||
"userCourseList": []
|
|
||||||
}
|
|
||||||
|
|
||||||
course_list = []
|
course_list = []
|
||||||
for course in user_courses:
|
for course in user_courses:
|
||||||
@ -502,11 +525,7 @@ class Mai2Base:
|
|||||||
tmp.pop("id")
|
tmp.pop("id")
|
||||||
course_list.append(tmp)
|
course_list.append(tmp)
|
||||||
|
|
||||||
return {
|
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": course_list}
|
||||||
"userId": data["userId"],
|
|
||||||
"nextIndex": 0,
|
|
||||||
"userCourseList": course_list
|
|
||||||
}
|
|
||||||
|
|
||||||
def handle_get_user_portrait_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_portrait_api_request(self, data: Dict) -> Dict:
|
||||||
# No support for custom pfps
|
# No support for custom pfps
|
||||||
@ -514,96 +533,103 @@ class Mai2Base:
|
|||||||
|
|
||||||
def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict:
|
||||||
friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"])
|
friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"])
|
||||||
friend_season_ranking_list = []
|
if friend_season_ranking is None:
|
||||||
next_index = 0
|
return {
|
||||||
|
"userId": data["userId"],
|
||||||
for x in range(data["nextIndex"], data["maxCount"] + data["nextIndex"]):
|
"nextIndex": 0,
|
||||||
try:
|
"userFriendSeasonRankingList": [],
|
||||||
friend_season_ranking_list.append(
|
|
||||||
{
|
|
||||||
"mapId": friend_season_ranking_list[x]["map_id"],
|
|
||||||
"distance": friend_season_ranking_list[x]["distance"],
|
|
||||||
"isLock": friend_season_ranking_list[x]["is_lock"],
|
|
||||||
"isClear": friend_season_ranking_list[x]["is_clear"],
|
|
||||||
"isComplete": friend_season_ranking_list[x]["is_complete"],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friend_season_ranking_list = []
|
||||||
|
next_idx = int(data["nextIndex"])
|
||||||
|
max_ct = int(data["maxCount"])
|
||||||
|
|
||||||
|
for x in range(next_idx, len(friend_season_ranking)):
|
||||||
|
tmp = friend_season_ranking[x]._asdict()
|
||||||
|
tmp.pop("user")
|
||||||
|
tmp.pop("id")
|
||||||
|
tmp["recordDate"] = datetime.strftime(
|
||||||
|
tmp["recordDate"], f"{Mai2Constants.DATE_TIME_FORMAT}.0"
|
||||||
)
|
)
|
||||||
except:
|
friend_season_ranking_list.append(tmp)
|
||||||
|
|
||||||
|
if len(friend_season_ranking_list) >= max_ct:
|
||||||
break
|
break
|
||||||
|
|
||||||
# We're capped and still have some left to go
|
if len(friend_season_ranking) >= next_idx + max_ct:
|
||||||
if (
|
next_idx += max_ct
|
||||||
len(friend_season_ranking_list) == data["maxCount"]
|
else:
|
||||||
and len(friend_season_ranking) > data["maxCount"] + data["nextIndex"]
|
next_idx = 0
|
||||||
):
|
|
||||||
next_index = data["maxCount"] + data["nextIndex"]
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"nextIndex": next_index,
|
"nextIndex": next_idx,
|
||||||
"userFriendSeasonRankingList": friend_season_ranking_list,
|
"userFriendSeasonRankingList": friend_season_ranking_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_map_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_map_api_request(self, data: Dict) -> Dict:
|
||||||
maps = self.data.item.get_maps(data["userId"])
|
maps = self.data.item.get_maps(data["userId"])
|
||||||
map_list = []
|
if maps is None:
|
||||||
next_index = 0
|
return {
|
||||||
|
"userId": data["userId"],
|
||||||
for x in range(data["nextIndex"], data["maxCount"] + data["nextIndex"]):
|
"nextIndex": 0,
|
||||||
try:
|
"userMapList": [],
|
||||||
map_list.append(
|
|
||||||
{
|
|
||||||
"mapId": maps[x]["map_id"],
|
|
||||||
"distance": maps[x]["distance"],
|
|
||||||
"isLock": maps[x]["is_lock"],
|
|
||||||
"isClear": maps[x]["is_clear"],
|
|
||||||
"isComplete": maps[x]["is_complete"],
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
except:
|
map_list = []
|
||||||
|
next_idx = int(data["nextIndex"])
|
||||||
|
max_ct = int(data["maxCount"])
|
||||||
|
|
||||||
|
for x in range(next_idx, len(maps)):
|
||||||
|
tmp = maps[x]._asdict()
|
||||||
|
tmp.pop("user")
|
||||||
|
tmp.pop("id")
|
||||||
|
map_list.append(tmp)
|
||||||
|
|
||||||
|
if len(map_list) >= max_ct:
|
||||||
break
|
break
|
||||||
|
|
||||||
# We're capped and still have some left to go
|
if len(maps) >= next_idx + max_ct:
|
||||||
if (
|
next_idx += max_ct
|
||||||
len(map_list) == data["maxCount"]
|
else:
|
||||||
and len(maps) > data["maxCount"] + data["nextIndex"]
|
next_idx = 0
|
||||||
):
|
|
||||||
next_index = data["maxCount"] + data["nextIndex"]
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"nextIndex": next_index,
|
"nextIndex": next_idx,
|
||||||
"userMapList": map_list,
|
"userMapList": map_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
||||||
login_bonuses = self.data.item.get_login_bonuses(data["userId"])
|
login_bonuses = self.data.item.get_login_bonuses(data["userId"])
|
||||||
login_bonus_list = []
|
if login_bonuses is None:
|
||||||
next_index = 0
|
return {
|
||||||
|
"userId": data["userId"],
|
||||||
for x in range(data["nextIndex"], data["maxCount"] + data["nextIndex"]):
|
"nextIndex": 0,
|
||||||
try:
|
"userLoginBonusList": [],
|
||||||
login_bonus_list.append(
|
|
||||||
{
|
|
||||||
"bonusId": login_bonuses[x]["bonus_id"],
|
|
||||||
"point": login_bonuses[x]["point"],
|
|
||||||
"isCurrent": login_bonuses[x]["is_current"],
|
|
||||||
"isComplete": login_bonuses[x]["is_complete"],
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
except:
|
login_bonus_list = []
|
||||||
|
next_idx = int(data["nextIndex"])
|
||||||
|
max_ct = int(data["maxCount"])
|
||||||
|
|
||||||
|
for x in range(next_idx, len(login_bonuses)):
|
||||||
|
tmp = login_bonuses[x]._asdict()
|
||||||
|
tmp.pop("user")
|
||||||
|
tmp.pop("id")
|
||||||
|
login_bonus_list.append(tmp)
|
||||||
|
|
||||||
|
if len(login_bonus_list) >= max_ct:
|
||||||
break
|
break
|
||||||
|
|
||||||
# We're capped and still have some left to go
|
if len(login_bonuses) >= next_idx + max_ct:
|
||||||
if (
|
next_idx += max_ct
|
||||||
len(login_bonus_list) == data["maxCount"]
|
else:
|
||||||
and len(login_bonuses) > data["maxCount"] + data["nextIndex"]
|
next_idx = 0
|
||||||
):
|
|
||||||
next_index = data["maxCount"] + data["nextIndex"]
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"nextIndex": next_index,
|
"nextIndex": next_idx,
|
||||||
"userLoginBonusList": login_bonus_list,
|
"userLoginBonusList": login_bonus_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,5 +655,5 @@ class Mai2Base:
|
|||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"nextIndex": next_index,
|
"nextIndex": next_index,
|
||||||
"userMusicList": [{"userMusicDetailList": music_detail_list}]
|
"userMusicList": [{"userMusicDetailList": music_detail_list}],
|
||||||
}
|
}
|
||||||
|
@ -30,14 +30,16 @@ class Mai2Constants:
|
|||||||
VER_MAIMAI_DX_SPLASH_PLUS = 3
|
VER_MAIMAI_DX_SPLASH_PLUS = 3
|
||||||
VER_MAIMAI_DX_UNIVERSE = 4
|
VER_MAIMAI_DX_UNIVERSE = 4
|
||||||
VER_MAIMAI_DX_UNIVERSE_PLUS = 5
|
VER_MAIMAI_DX_UNIVERSE_PLUS = 5
|
||||||
|
VER_MAIMAI_DX_FESTIVAL = 6
|
||||||
|
|
||||||
VERSION_STRING = (
|
VERSION_STRING = (
|
||||||
"maimai Delux",
|
"maimai DX",
|
||||||
"maimai Delux PLUS",
|
"maimai DX PLUS",
|
||||||
"maimai Delux Splash",
|
"maimai DX Splash",
|
||||||
"maimai Delux Splash PLUS",
|
"maimai DX Splash PLUS",
|
||||||
"maimai Delux Universe",
|
"maimai DX Universe",
|
||||||
"maimai Delux Universe PLUS",
|
"maimai DX Universe PLUS",
|
||||||
|
"maimai DX Festival"
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
31
titles/mai2/festival.py
Normal file
31
titles/mai2/festival.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from core.config import CoreConfig
|
||||||
|
from titles.mai2.universeplus import Mai2UniversePlus
|
||||||
|
from titles.mai2.const import Mai2Constants
|
||||||
|
from titles.mai2.config import Mai2Config
|
||||||
|
|
||||||
|
|
||||||
|
class Mai2Festival(Mai2UniversePlus):
|
||||||
|
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
|
||||||
|
super().__init__(cfg, game_cfg)
|
||||||
|
self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
|
||||||
|
|
||||||
|
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.36
|
||||||
|
user_data["lastDataVersion"] = "1.30.00"
|
||||||
|
return user_data
|
||||||
|
|
||||||
|
def handle_user_login_api_request(self, data: Dict) -> Dict:
|
||||||
|
user_login = super().handle_user_login_api_request(data)
|
||||||
|
# useless?
|
||||||
|
user_login["Bearer"] = "ARTEMiSTOKEN"
|
||||||
|
return user_login
|
||||||
|
|
||||||
|
def handle_get_user_recommend_rate_music_api_request(self, data: Dict) -> Dict:
|
||||||
|
return {"userId": data["userId"], "userRecommendRateMusicIdList": []}
|
||||||
|
|
||||||
|
def handle_get_user_recommend_select_music_api_request(self, data: Dict) -> Dict:
|
||||||
|
return {"userId": data["userId"], "userRecommendSelectionMusicIdList": []}
|
@ -10,6 +10,7 @@ from os import path
|
|||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
|
from core.utils import Utils
|
||||||
from titles.mai2.config import Mai2Config
|
from titles.mai2.config import Mai2Config
|
||||||
from titles.mai2.const import Mai2Constants
|
from titles.mai2.const import Mai2Constants
|
||||||
from titles.mai2.base import Mai2Base
|
from titles.mai2.base import Mai2Base
|
||||||
@ -18,6 +19,7 @@ from titles.mai2.splash import Mai2Splash
|
|||||||
from titles.mai2.splashplus import Mai2SplashPlus
|
from titles.mai2.splashplus import Mai2SplashPlus
|
||||||
from titles.mai2.universe import Mai2Universe
|
from titles.mai2.universe import Mai2Universe
|
||||||
from titles.mai2.universeplus import Mai2UniversePlus
|
from titles.mai2.universeplus import Mai2UniversePlus
|
||||||
|
from titles.mai2.festival import Mai2Festival
|
||||||
|
|
||||||
|
|
||||||
class Mai2Servlet:
|
class Mai2Servlet:
|
||||||
@ -30,12 +32,13 @@ class Mai2Servlet:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.versions = [
|
self.versions = [
|
||||||
Mai2Base(core_cfg, self.game_cfg),
|
Mai2Base,
|
||||||
Mai2Plus(core_cfg, self.game_cfg),
|
Mai2Plus,
|
||||||
Mai2Splash(core_cfg, self.game_cfg),
|
Mai2Splash,
|
||||||
Mai2SplashPlus(core_cfg, self.game_cfg),
|
Mai2SplashPlus,
|
||||||
Mai2Universe(core_cfg, self.game_cfg),
|
Mai2Universe,
|
||||||
Mai2UniversePlus(core_cfg, self.game_cfg),
|
Mai2UniversePlus,
|
||||||
|
Mai2Festival
|
||||||
]
|
]
|
||||||
|
|
||||||
self.logger = logging.getLogger("mai2")
|
self.logger = logging.getLogger("mai2")
|
||||||
@ -97,6 +100,7 @@ class Mai2Servlet:
|
|||||||
url_split = url_path.split("/")
|
url_split = url_path.split("/")
|
||||||
internal_ver = 0
|
internal_ver = 0
|
||||||
endpoint = url_split[len(url_split) - 1]
|
endpoint = url_split[len(url_split) - 1]
|
||||||
|
client_ip = Utils.get_ip_addr(request)
|
||||||
|
|
||||||
if version < 105: # 1.0
|
if version < 105: # 1.0
|
||||||
internal_ver = Mai2Constants.VER_MAIMAI_DX
|
internal_ver = Mai2Constants.VER_MAIMAI_DX
|
||||||
@ -108,8 +112,10 @@ class Mai2Servlet:
|
|||||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_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
|
internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE
|
||||||
elif version >= 125: # Universe Plus
|
elif version >= 125 and version < 130: # Universe Plus
|
||||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
|
internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
|
||||||
|
elif version >= 130: # Festival
|
||||||
|
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
|
||||||
|
|
||||||
if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32:
|
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
|
# If we get a 32 character long hex string, it's a hash and we're
|
||||||
@ -128,16 +134,21 @@ class Mai2Servlet:
|
|||||||
|
|
||||||
req_data = json.loads(unzip)
|
req_data = json.loads(unzip)
|
||||||
|
|
||||||
self.logger.info(f"v{version} {endpoint} request - {req_data}")
|
self.logger.info(
|
||||||
|
f"v{version} {endpoint} request from {client_ip}"
|
||||||
|
)
|
||||||
|
self.logger.debug(req_data)
|
||||||
|
|
||||||
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
||||||
|
handler_cls = self.versions[internal_ver](self.core_cfg, self.game_cfg)
|
||||||
|
|
||||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
if not hasattr(handler_cls, func_to_find):
|
||||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||||
return zlib.compress(b'{"returnCode": 1}')
|
resp = {"returnCode": 1}
|
||||||
|
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
handler = getattr(handler_cls, func_to_find)
|
||||||
resp = handler(req_data)
|
resp = handler(req_data)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -147,6 +158,6 @@ class Mai2Servlet:
|
|||||||
if resp == None:
|
if resp == None:
|
||||||
resp = {"returnCode": 1}
|
resp = {"returnCode": 1}
|
||||||
|
|
||||||
self.logger.info(f"Response {resp}")
|
self.logger.debug(f"Response {resp}")
|
||||||
|
|
||||||
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||||
|
@ -29,7 +29,7 @@ class Mai2Reader(BaseReader):
|
|||||||
f"Start importer for {Mai2Constants.game_ver_to_string(version)}"
|
f"Start importer for {Mai2Constants.game_ver_to_string(version)}"
|
||||||
)
|
)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
self.logger.error(f"Invalid maidx version {version}")
|
self.logger.error(f"Invalid maimai DX version {version}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
def read(self) -> None:
|
def read(self) -> None:
|
||||||
@ -43,6 +43,7 @@ class Mai2Reader(BaseReader):
|
|||||||
for dir in data_dirs:
|
for dir in data_dirs:
|
||||||
self.logger.info(f"Read from {dir}")
|
self.logger.info(f"Read from {dir}")
|
||||||
self.get_events(f"{dir}/event")
|
self.get_events(f"{dir}/event")
|
||||||
|
self.disable_events(f"{dir}/information", f"{dir}/scoreRanking")
|
||||||
self.read_music(f"{dir}/music")
|
self.read_music(f"{dir}/music")
|
||||||
self.read_tickets(f"{dir}/ticket")
|
self.read_tickets(f"{dir}/ticket")
|
||||||
|
|
||||||
@ -64,6 +65,64 @@ class Mai2Reader(BaseReader):
|
|||||||
)
|
)
|
||||||
self.logger.info(f"Added event {id}...")
|
self.logger.info(f"Added event {id}...")
|
||||||
|
|
||||||
|
def disable_events(
|
||||||
|
self, base_information_dir: str, base_score_ranking_dir: str
|
||||||
|
) -> None:
|
||||||
|
self.logger.info(f"Reading disabled events from {base_information_dir}...")
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(base_information_dir):
|
||||||
|
for dir in dirs:
|
||||||
|
if os.path.exists(f"{root}/{dir}/Information.xml"):
|
||||||
|
with open(f"{root}/{dir}/Information.xml", encoding="utf-8") as f:
|
||||||
|
troot = ET.fromstring(f.read())
|
||||||
|
|
||||||
|
event_id = int(troot.find("name").find("id").text)
|
||||||
|
|
||||||
|
self.data.static.toggle_game_event(
|
||||||
|
self.version, event_id, toggle=False
|
||||||
|
)
|
||||||
|
self.logger.info(f"Disabled event {event_id}...")
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(base_score_ranking_dir):
|
||||||
|
for dir in dirs:
|
||||||
|
if os.path.exists(f"{root}/{dir}/ScoreRanking.xml"):
|
||||||
|
with open(f"{root}/{dir}/ScoreRanking.xml", encoding="utf-8") as f:
|
||||||
|
troot = ET.fromstring(f.read())
|
||||||
|
|
||||||
|
event_id = int(troot.find("eventName").find("id").text)
|
||||||
|
|
||||||
|
self.data.static.toggle_game_event(
|
||||||
|
self.version, event_id, toggle=False
|
||||||
|
)
|
||||||
|
self.logger.info(f"Disabled event {event_id}...")
|
||||||
|
|
||||||
|
# manually disable events wich are known to be problematic
|
||||||
|
for event_id in [
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
220311,
|
||||||
|
220312,
|
||||||
|
220313,
|
||||||
|
220314,
|
||||||
|
220315,
|
||||||
|
220316,
|
||||||
|
220317,
|
||||||
|
220318,
|
||||||
|
20121821,
|
||||||
|
21121651,
|
||||||
|
22091511,
|
||||||
|
22091512,
|
||||||
|
22091513,
|
||||||
|
22091514,
|
||||||
|
22091515,
|
||||||
|
22091516,
|
||||||
|
22091517,
|
||||||
|
22091518,
|
||||||
|
22091519,
|
||||||
|
]:
|
||||||
|
self.data.static.toggle_game_event(self.version, event_id, toggle=False)
|
||||||
|
self.logger.info(f"Disabled event {event_id}...")
|
||||||
|
|
||||||
def read_music(self, base_dir: str) -> None:
|
def read_music(self, base_dir: str) -> None:
|
||||||
self.logger.info(f"Reading music from {base_dir}...")
|
self.logger.info(f"Reading music from {base_dir}...")
|
||||||
|
|
||||||
|
@ -71,12 +71,12 @@ map = Table(
|
|||||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
),
|
),
|
||||||
Column("map_id", Integer, nullable=False),
|
Column("mapId", Integer, nullable=False),
|
||||||
Column("distance", Integer, nullable=False),
|
Column("distance", Integer, nullable=False),
|
||||||
Column("is_lock", Boolean, nullable=False, server_default="0"),
|
Column("isLock", Boolean, nullable=False, server_default="0"),
|
||||||
Column("is_clear", Boolean, nullable=False, server_default="0"),
|
Column("isClear", Boolean, nullable=False, server_default="0"),
|
||||||
Column("is_complete", Boolean, nullable=False, server_default="0"),
|
Column("isComplete", Boolean, nullable=False, server_default="0"),
|
||||||
UniqueConstraint("user", "map_id", name="mai2_item_map_uk"),
|
UniqueConstraint("user", "mapId", name="mai2_item_map_uk"),
|
||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -89,11 +89,11 @@ login_bonus = Table(
|
|||||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
),
|
),
|
||||||
Column("bonus_id", Integer, nullable=False),
|
Column("bonusId", Integer, nullable=False),
|
||||||
Column("point", Integer, nullable=False),
|
Column("point", Integer, nullable=False),
|
||||||
Column("is_current", Boolean, nullable=False, server_default="0"),
|
Column("isCurrent", Boolean, nullable=False, server_default="0"),
|
||||||
Column("is_complete", Boolean, nullable=False, server_default="0"),
|
Column("isComplete", Boolean, nullable=False, server_default="0"),
|
||||||
UniqueConstraint("user", "bonus_id", name="mai2_item_login_bonus_uk"),
|
UniqueConstraint("user", "bonusId", name="mai2_item_login_bonus_uk"),
|
||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -106,13 +106,15 @@ friend_season_ranking = Table(
|
|||||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
),
|
),
|
||||||
Column("season_id", Integer, nullable=False),
|
Column("seasonId", Integer, nullable=False),
|
||||||
Column("point", Integer, nullable=False),
|
Column("point", Integer, nullable=False),
|
||||||
Column("rank", Integer, nullable=False),
|
Column("rank", Integer, nullable=False),
|
||||||
Column("reward_get", Boolean, nullable=False),
|
Column("rewardGet", Boolean, nullable=False),
|
||||||
Column("user_name", String(8), nullable=False),
|
Column("userName", String(8), nullable=False),
|
||||||
Column("record_date", String(255), nullable=False),
|
Column("recordDate", TIMESTAMP, nullable=False),
|
||||||
UniqueConstraint("user", "season_id", "user_name", name="mai2_item_login_bonus_uk"),
|
UniqueConstraint(
|
||||||
|
"user", "seasonId", "userName", name="mai2_item_friend_season_ranking_uk"
|
||||||
|
),
|
||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -244,16 +246,16 @@ class Mai2ItemData(BaseData):
|
|||||||
) -> None:
|
) -> None:
|
||||||
sql = insert(login_bonus).values(
|
sql = insert(login_bonus).values(
|
||||||
user=user_id,
|
user=user_id,
|
||||||
bonus_id=bonus_id,
|
bonusId=bonus_id,
|
||||||
point=point,
|
point=point,
|
||||||
is_current=is_current,
|
isCurrent=is_current,
|
||||||
is_complete=is_complete,
|
isComplete=is_complete,
|
||||||
)
|
)
|
||||||
|
|
||||||
conflict = sql.on_duplicate_key_update(
|
conflict = sql.on_duplicate_key_update(
|
||||||
point=point,
|
point=point,
|
||||||
is_current=is_current,
|
isCurrent=is_current,
|
||||||
is_complete=is_complete,
|
isComplete=is_complete,
|
||||||
)
|
)
|
||||||
|
|
||||||
result = self.execute(conflict)
|
result = self.execute(conflict)
|
||||||
@ -293,18 +295,18 @@ class Mai2ItemData(BaseData):
|
|||||||
) -> None:
|
) -> None:
|
||||||
sql = insert(map).values(
|
sql = insert(map).values(
|
||||||
user=user_id,
|
user=user_id,
|
||||||
map_id=map_id,
|
mapId=map_id,
|
||||||
distance=distance,
|
distance=distance,
|
||||||
is_lock=is_lock,
|
isLock=is_lock,
|
||||||
is_clear=is_clear,
|
isClear=is_clear,
|
||||||
is_complete=is_complete,
|
isComplete=is_complete,
|
||||||
)
|
)
|
||||||
|
|
||||||
conflict = sql.on_duplicate_key_update(
|
conflict = sql.on_duplicate_key_update(
|
||||||
distance=distance,
|
distance=distance,
|
||||||
is_lock=is_lock,
|
isLock=is_lock,
|
||||||
is_clear=is_clear,
|
isClear=is_clear,
|
||||||
is_complete=is_complete,
|
isComplete=is_complete,
|
||||||
)
|
)
|
||||||
|
|
||||||
result = self.execute(conflict)
|
result = self.execute(conflict)
|
||||||
@ -324,7 +326,7 @@ class Mai2ItemData(BaseData):
|
|||||||
return result.fetchall()
|
return result.fetchall()
|
||||||
|
|
||||||
def get_map(self, user_id: int, map_id: int) -> Optional[Row]:
|
def get_map(self, user_id: int, map_id: int) -> Optional[Row]:
|
||||||
sql = map.select(and_(map.c.user == user_id, map.c.map_id == map_id))
|
sql = map.select(and_(map.c.user == user_id, map.c.mapId == map_id))
|
||||||
|
|
||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -341,16 +343,16 @@ class Mai2ItemData(BaseData):
|
|||||||
) -> None:
|
) -> None:
|
||||||
sql = insert(character).values(
|
sql = insert(character).values(
|
||||||
user=user_id,
|
user=user_id,
|
||||||
character_id=character_id,
|
characterId=character_id,
|
||||||
level=level,
|
level=level,
|
||||||
awakening=awakening,
|
awakening=awakening,
|
||||||
use_count=use_count,
|
useCount=use_count,
|
||||||
)
|
)
|
||||||
|
|
||||||
conflict = sql.on_duplicate_key_update(
|
conflict = sql.on_duplicate_key_update(
|
||||||
level=level,
|
level=level,
|
||||||
awakening=awakening,
|
awakening=awakening,
|
||||||
use_count=use_count,
|
useCount=use_count,
|
||||||
)
|
)
|
||||||
|
|
||||||
result = self.execute(conflict)
|
result = self.execute(conflict)
|
||||||
@ -385,7 +387,25 @@ class Mai2ItemData(BaseData):
|
|||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None:
|
if result is None:
|
||||||
return None
|
return None
|
||||||
return result.fetchone()
|
return result.fetchall()
|
||||||
|
|
||||||
|
def put_friend_season_ranking(
|
||||||
|
self, aime_id: int, friend_season_ranking_data: Dict
|
||||||
|
) -> Optional[int]:
|
||||||
|
sql = insert(friend_season_ranking).values(
|
||||||
|
user=aime_id, **friend_season_ranking_data
|
||||||
|
)
|
||||||
|
|
||||||
|
conflict = sql.on_duplicate_key_update(**friend_season_ranking_data)
|
||||||
|
result = self.execute(conflict)
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
self.logger.warn(
|
||||||
|
f"put_friend_season_ranking: failed to insert",
|
||||||
|
f"friend_season_ranking! aime_id: {aime_id}"
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
return result.lastrowid
|
||||||
|
|
||||||
def put_favorite(
|
def put_favorite(
|
||||||
self, user_id: int, kind: int, item_id_list: List[int]
|
self, user_id: int, kind: int, item_id_list: List[int]
|
||||||
|
@ -158,6 +158,7 @@ extend = Table(
|
|||||||
Column("sortMusicSetting", Integer),
|
Column("sortMusicSetting", Integer),
|
||||||
Column("selectedCardList", JSON),
|
Column("selectedCardList", JSON),
|
||||||
Column("encountMapNpcList", JSON),
|
Column("encountMapNpcList", JSON),
|
||||||
|
Column("playStatusSetting", Integer, server_default="0"),
|
||||||
UniqueConstraint("user", "version", name="mai2_profile_extend_uk"),
|
UniqueConstraint("user", "version", name="mai2_profile_extend_uk"),
|
||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
)
|
)
|
||||||
@ -178,6 +179,7 @@ option = Table(
|
|||||||
Column("slideSpeed", Integer),
|
Column("slideSpeed", Integer),
|
||||||
Column("touchSpeed", Integer),
|
Column("touchSpeed", Integer),
|
||||||
Column("tapDesign", Integer),
|
Column("tapDesign", Integer),
|
||||||
|
Column("tapSe", Integer, server_default="0"),
|
||||||
Column("holdDesign", Integer),
|
Column("holdDesign", Integer),
|
||||||
Column("slideDesign", Integer),
|
Column("slideDesign", Integer),
|
||||||
Column("starType", Integer),
|
Column("starType", Integer),
|
||||||
@ -298,8 +300,8 @@ class Mai2ProfileData(BaseData):
|
|||||||
|
|
||||||
def get_profile_detail(self, user_id: int, version: int) -> Optional[Row]:
|
def get_profile_detail(self, user_id: int, version: int) -> Optional[Row]:
|
||||||
sql = select(detail).where(
|
sql = select(detail).where(
|
||||||
and_(detail.c.user == user_id, detail.c.version == version)
|
and_(detail.c.user == user_id, detail.c.version <= version)
|
||||||
)
|
).order_by(detail.c.version.desc())
|
||||||
|
|
||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -323,8 +325,8 @@ class Mai2ProfileData(BaseData):
|
|||||||
|
|
||||||
def get_profile_ghost(self, user_id: int, version: int) -> Optional[Row]:
|
def get_profile_ghost(self, user_id: int, version: int) -> Optional[Row]:
|
||||||
sql = select(ghost).where(
|
sql = select(ghost).where(
|
||||||
and_(ghost.c.user == user_id, ghost.c.version_int == version)
|
and_(ghost.c.user == user_id, ghost.c.version_int <= version)
|
||||||
)
|
).order_by(ghost.c.version.desc())
|
||||||
|
|
||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -348,8 +350,8 @@ class Mai2ProfileData(BaseData):
|
|||||||
|
|
||||||
def get_profile_extend(self, user_id: int, version: int) -> Optional[Row]:
|
def get_profile_extend(self, user_id: int, version: int) -> Optional[Row]:
|
||||||
sql = select(extend).where(
|
sql = select(extend).where(
|
||||||
and_(extend.c.user == user_id, extend.c.version == version)
|
and_(extend.c.user == user_id, extend.c.version <= version)
|
||||||
)
|
).order_by(extend.c.version.desc())
|
||||||
|
|
||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -373,8 +375,8 @@ class Mai2ProfileData(BaseData):
|
|||||||
|
|
||||||
def get_profile_option(self, user_id: int, version: int) -> Optional[Row]:
|
def get_profile_option(self, user_id: int, version: int) -> Optional[Row]:
|
||||||
sql = select(option).where(
|
sql = select(option).where(
|
||||||
and_(option.c.user == user_id, option.c.version == version)
|
and_(option.c.user == user_id, option.c.version <= version)
|
||||||
)
|
).order_by(option.c.version.desc())
|
||||||
|
|
||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -398,8 +400,8 @@ class Mai2ProfileData(BaseData):
|
|||||||
|
|
||||||
def get_profile_rating(self, user_id: int, version: int) -> Optional[Row]:
|
def get_profile_rating(self, user_id: int, version: int) -> Optional[Row]:
|
||||||
sql = select(rating).where(
|
sql = select(rating).where(
|
||||||
and_(rating.c.user == user_id, rating.c.version == version)
|
and_(rating.c.user == user_id, rating.c.version <= version)
|
||||||
)
|
).order_by(rating.c.version.desc())
|
||||||
|
|
||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None:
|
if result is None:
|
||||||
|
@ -25,6 +25,7 @@ best_score = Table(
|
|||||||
Column("syncStatus", Integer),
|
Column("syncStatus", Integer),
|
||||||
Column("deluxscoreMax", Integer),
|
Column("deluxscoreMax", Integer),
|
||||||
Column("scoreRank", Integer),
|
Column("scoreRank", Integer),
|
||||||
|
Column("extNum1", Integer, server_default="0"),
|
||||||
UniqueConstraint("user", "musicId", "level", name="mai2_score_best_uk"),
|
UniqueConstraint("user", "musicId", "level", name="mai2_score_best_uk"),
|
||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
)
|
)
|
||||||
@ -143,6 +144,7 @@ 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("trialPlayAchievement", Integer),
|
Column("trialPlayAchievement", Integer),
|
||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
)
|
)
|
||||||
|
@ -16,6 +16,7 @@ event = Table(
|
|||||||
Column("eventId", Integer),
|
Column("eventId", Integer),
|
||||||
Column("type", Integer),
|
Column("type", Integer),
|
||||||
Column("name", String(255)),
|
Column("name", String(255)),
|
||||||
|
Column("startDate", TIMESTAMP, server_default=func.now()),
|
||||||
Column("enabled", Boolean, server_default="1"),
|
Column("enabled", Boolean, server_default="1"),
|
||||||
UniqueConstraint("version", "eventId", "type", name="mai2_static_event_uk"),
|
UniqueConstraint("version", "eventId", "type", name="mai2_static_event_uk"),
|
||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
@ -108,17 +109,17 @@ class Mai2StaticData(BaseData):
|
|||||||
return None
|
return None
|
||||||
return result.fetchall()
|
return result.fetchall()
|
||||||
|
|
||||||
def toggle_game_events(
|
def toggle_game_event(
|
||||||
self, version: int, event_id: int, toggle: bool
|
self, version: int, event_id: int, toggle: bool
|
||||||
) -> Optional[List]:
|
) -> Optional[List]:
|
||||||
sql = event.update(
|
sql = event.update(
|
||||||
and_(event.c.version == version, event.c.event_id == event_id)
|
and_(event.c.version == version, event.c.eventId == event_id)
|
||||||
).values(enabled=int(toggle))
|
).values(enabled=int(toggle))
|
||||||
|
|
||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None:
|
if result is None:
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
f"toggle_game_events: Failed to update event! event_id {event_id} toggle {toggle}"
|
f"toggle_game_event: Failed to update event! event_id {event_id} toggle {toggle}"
|
||||||
)
|
)
|
||||||
return result.last_updated_params()
|
return result.last_updated_params()
|
||||||
|
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
from typing import Any, List, Dict
|
from typing import Dict
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import pytz
|
|
||||||
import json
|
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from titles.mai2.universe import Mai2Universe
|
from titles.mai2.universe import Mai2Universe
|
||||||
|
Loading…
Reference in New Issue
Block a user