mai2: reimplement pre-dx versions

This commit is contained in:
Hay1tsme 2023-05-06 19:04:10 -04:00
parent 8149f09a40
commit b34b441ba8
12 changed files with 840 additions and 260 deletions

View File

@ -0,0 +1,26 @@
DELETE FROM mai2_static_event WHERE version < 13;
UPDATE mai2_static_event SET version = version - 13 WHERE version >= 13;
DELETE FROM mai2_static_music WHERE version < 13;
UPDATE mai2_static_music SET version = version - 13 WHERE version >= 13;
DELETE FROM mai2_static_ticket WHERE version < 13;
UPDATE mai2_static_ticket SET version = version - 13 WHERE version >= 13;
DELETE FROM mai2_static_cards WHERE version < 13;
UPDATE mai2_static_cards SET version = version - 13 WHERE version >= 13;
DELETE FROM mai2_profile_detail WHERE version < 13;
UPDATE mai2_profile_detail SET version = version - 13 WHERE version >= 13;
DELETE FROM mai2_profile_extend WHERE version < 13;
UPDATE mai2_profile_extend SET version = version - 13 WHERE version >= 13;
DELETE FROM mai2_profile_option WHERE version < 13;
UPDATE mai2_profile_option SET version = version - 13 WHERE version >= 13;
DELETE FROM mai2_profile_ghost WHERE version < 13;
UPDATE mai2_profile_ghost SET version = version - 13 WHERE version >= 13;
DELETE FROM mai2_profile_rating WHERE version < 13;
UPDATE mai2_profile_rating SET version = version - 13 WHERE version >= 13;

View File

@ -0,0 +1,17 @@
UPDATE mai2_static_event SET version = version + 13 WHERE version < 1000;
UPDATE mai2_static_music SET version = version + 13 WHERE version < 1000;
UPDATE mai2_static_ticket SET version = version + 13 WHERE version < 1000;
UPDATE mai2_static_cards SET version = version + 13 WHERE version < 1000;
UPDATE mai2_profile_detail SET version = version + 13 WHERE version < 1000;
UPDATE mai2_profile_extend SET version = version + 13 WHERE version < 1000;
UPDATE mai2_profile_option SET version = version + 13 WHERE version < 1000;
UPDATE mai2_profile_ghost SET version = version + 13 WHERE version < 1000;
UPDATE mai2_profile_rating SET version = version + 13 WHERE version < 1000;

View File

@ -17,18 +17,18 @@ class Mai2Base:
self.logger = logging.getLogger("mai2") self.logger = logging.getLogger("mai2")
if self.core_config.server.is_develop and self.core_config.title.port > 0: if self.core_config.server.is_develop and self.core_config.title.port > 0:
self.old_server = f"http://{self.core_config.title.hostname}:{self.core_config.title.port}/SDEY/100/" self.old_server = f"http://{self.core_config.title.hostname}:{self.core_config.title.port}/SDEY/197/"
else: else:
self.old_server = f"http://{self.core_config.title.hostname}/SDEY/100/" self.old_server = f"http://{self.core_config.title.hostname}/SDEY/197/"
def handle_get_game_setting_api_request(self, data: Dict): def handle_get_game_setting_api_request(self, data: Dict):
# TODO: See if making this epoch 0 breaks things # TODO: See if making this epoch 0 breaks things
reboot_start = date.strftime( reboot_start = date.strftime(
datetime.now() + timedelta(hours=3), Mai2Constants.DATE_TIME_FORMAT datetime.fromtimestamp(0.0), Mai2Constants.DATE_TIME_FORMAT
) )
reboot_end = date.strftime( reboot_end = date.strftime(
datetime.now() + timedelta(hours=4), Mai2Constants.DATE_TIME_FORMAT datetime.fromtimestamp(0.0) + timedelta(hours=1), Mai2Constants.DATE_TIME_FORMAT
) )
return { return {
"gameSetting": { "gameSetting": {
@ -39,9 +39,9 @@ class Mai2Base:
"movieUploadLimit": 10000, "movieUploadLimit": 10000,
"movieStatus": 0, "movieStatus": 0,
"movieServerUri": "", "movieServerUri": "",
"deliverServerUri": "", "deliverServerUri": self.old_server + "deliver",
"oldServerUri": self.old_server, "oldServerUri": self.old_server + "old",
"usbDlServerUri": "", "usbDlServerUri": self.old_server + "usbdl",
"rebootInterval": 0, "rebootInterval": 0,
}, },
"isAouAccession": "true", "isAouAccession": "true",
@ -56,8 +56,9 @@ class Mai2Base:
def handle_get_game_event_api_request(self, data: Dict) -> Dict: def handle_get_game_event_api_request(self, data: Dict) -> Dict:
events = self.data.static.get_enabled_events(self.version) events = self.data.static.get_enabled_events(self.version)
print(self.version)
events_lst = [] events_lst = []
if events is None: if events is None or not events:
self.logger.warn("No enabled events, did you run the reader?") 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": []}
@ -127,28 +128,20 @@ class Mai2Base:
"userId": data["userId"], "userId": data["userId"],
"userName": profile["userName"], "userName": profile["userName"],
"isLogin": False, "isLogin": False,
"lastGameId": profile["lastGameId"],
"lastDataVersion": profile["lastDataVersion"], "lastDataVersion": profile["lastDataVersion"],
"lastRomVersion": profile["lastRomVersion"],
"lastLoginDate": profile["lastLoginDate"], "lastLoginDate": profile["lastLoginDate"],
"lastPlayDate": profile["lastPlayDate"], "lastPlayDate": profile["lastPlayDate"],
"playerRating": profile["playerRating"], "playerRating": profile["playerRating"],
"nameplateId": 0, # Unused "nameplateId": 0, # Unused
"frameId": profile["frameId"],
"iconId": profile["iconId"], "iconId": profile["iconId"],
"trophyId": 0, # Unused "trophyId": 0, # Unused
"partnerId": profile["partnerId"], "partnerId": profile["partnerId"],
"frameId": profile["frameId"], "dispRate": option["dispRate"], # 0: all, 1: dispRate, 2: dispDan, 3: hide
"dispRate": option[ "dispRank": 0, # TODO
"dispRate" "dispHomeRanker": 0, # TODO
], # 0: all/begin, 1: disprate, 2: dispDan, 3: hide, 4: end "dispTotalLv": 0, # TODO
"totalAwake": profile["totalAwake"], "totalLv": 0, # TODO
"isNetMember": profile["isNetMember"],
"dailyBonusDate": profile["dailyBonusDate"],
"headPhoneVolume": option["headPhoneVolume"],
"isInherit": False, # Not sure what this is or does??
"banState": profile["banState"]
if profile["banState"] is not None
else 0, # New with uni+
} }
def handle_user_login_api_request(self, data: Dict) -> Dict: def handle_user_login_api_request(self, data: Dict) -> Dict:
@ -169,7 +162,6 @@ class Mai2Base:
"lastLoginDate": lastLoginDate, "lastLoginDate": lastLoginDate,
"loginCount": loginCt, "loginCount": loginCt,
"consecutiveLoginCount": 0, # We don't really have a way to track this... "consecutiveLoginCount": 0, # We don't really have a way to track this...
"loginId": loginCt, # Used with the playlog!
} }
def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict: def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict:

View File

@ -31,39 +31,29 @@ class Mai2Constants:
CONFIG_NAME = "mai2.yaml" CONFIG_NAME = "mai2.yaml"
VER_MAIMAI = 1000 VER_MAIMAI = 0
VER_MAIMAI_PLUS = 1001 VER_MAIMAI_PLUS = 1
VER_MAIMAI_GREEN = 1002 VER_MAIMAI_GREEN = 2
VER_MAIMAI_GREEN_PLUS = 1003 VER_MAIMAI_GREEN_PLUS = 3
VER_MAIMAI_ORANGE = 1004 VER_MAIMAI_ORANGE = 4
VER_MAIMAI_ORANGE_PLUS = 1005 VER_MAIMAI_ORANGE_PLUS = 5
VER_MAIMAI_PINK = 1006 VER_MAIMAI_PINK = 6
VER_MAIMAI_PINK_PLUS = 1007 VER_MAIMAI_PINK_PLUS = 7
VER_MAIMAI_MURASAKI = 1008 VER_MAIMAI_MURASAKI = 8
VER_MAIMAI_MURASAKI_PLUS = 1009 VER_MAIMAI_MURASAKI_PLUS = 9
VER_MAIMAI_MILK = 1010 VER_MAIMAI_MILK = 10
VER_MAIMAI_MILK_PLUS = 1011 VER_MAIMAI_MILK_PLUS = 11
VER_MAIMAI_FINALE = 1012 VER_MAIMAI_FINALE = 12
VER_MAIMAI_DX = 0 VER_MAIMAI_DX = 13
VER_MAIMAI_DX_PLUS = 1 VER_MAIMAI_DX_PLUS = 14
VER_MAIMAI_DX_SPLASH = 2 VER_MAIMAI_DX_SPLASH = 15
VER_MAIMAI_DX_SPLASH_PLUS = 3 VER_MAIMAI_DX_SPLASH_PLUS = 16
VER_MAIMAI_DX_UNIVERSE = 4 VER_MAIMAI_DX_UNIVERSE = 17
VER_MAIMAI_DX_UNIVERSE_PLUS = 5 VER_MAIMAI_DX_UNIVERSE_PLUS = 18
VER_MAIMAI_DX_FESTIVAL = 6 VER_MAIMAI_DX_FESTIVAL = 19
VERSION_STRING = ( VERSION_STRING = (
"maimai DX",
"maimai DX PLUS",
"maimai DX Splash",
"maimai DX Splash PLUS",
"maimai DX Universe",
"maimai DX Universe PLUS",
"maimai DX Festival",
)
VERSION_STRING_OLD = (
"maimai", "maimai",
"maimai PLUS", "maimai PLUS",
"maimai GreeN", "maimai GreeN",
@ -77,10 +67,15 @@ class Mai2Constants:
"maimai MiLK", "maimai MiLK",
"maimai MiLK PLUS", "maimai MiLK PLUS",
"maimai FiNALE", "maimai FiNALE",
"maimai DX",
"maimai DX PLUS",
"maimai DX Splash",
"maimai DX Splash PLUS",
"maimai DX Universe",
"maimai DX Universe PLUS",
"maimai DX Festival",
) )
@classmethod @classmethod
def game_ver_to_string(cls, ver: int): def game_ver_to_string(cls, ver: int):
if ver >= 1000:
return cls.VERSION_STRING_OLD[ver - 1000]
return cls.VERSION_STRING[ver] return cls.VERSION_STRING[ver]

View File

@ -2,6 +2,7 @@ from typing import Any, List, Dict
from datetime import datetime, timedelta from datetime import datetime, timedelta
import pytz import pytz
import json import json
from random import randint
from core.config import CoreConfig from core.config import CoreConfig
from titles.mai2.base import Mai2Base from titles.mai2.base import Mai2Base
@ -13,3 +14,727 @@ class Mai2DX(Mai2Base):
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None: def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
super().__init__(cfg, game_cfg) super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX self.version = Mai2Constants.VER_MAIMAI_DX
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_detail(data["userId"], self.version)
o = self.data.profile.get_profile_option(data["userId"], self.version)
if p is None or o is None:
return {} # Register
profile = p._asdict()
option = o._asdict()
return {
"userId": data["userId"],
"userName": profile["userName"],
"isLogin": False,
"lastGameId": profile["lastGameId"],
"lastDataVersion": profile["lastDataVersion"],
"lastRomVersion": profile["lastRomVersion"],
"lastLoginDate": profile["lastLoginDate"],
"lastPlayDate": profile["lastPlayDate"],
"playerRating": profile["playerRating"],
"nameplateId": 0, # Unused
"iconId": profile["iconId"],
"trophyId": 0, # Unused
"partnerId": profile["partnerId"],
"frameId": profile["frameId"],
"dispRate": option[
"dispRate"
], # 0: all/begin, 1: disprate, 2: dispDan, 3: hide, 4: end
"totalAwake": profile["totalAwake"],
"isNetMember": profile["isNetMember"],
"dailyBonusDate": profile["dailyBonusDate"],
"headPhoneVolume": option["headPhoneVolume"],
"isInherit": False, # Not sure what this is or does??
"banState": profile["banState"]
if profile["banState"] is not None
else 0, # New with uni+
}
def handle_user_login_api_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
if profile is not None:
lastLoginDate = profile["lastLoginDate"]
loginCt = profile["playCount"]
if "regionId" in data:
self.data.profile.put_profile_region(data["userId"], data["regionId"])
else:
loginCt = 0
lastLoginDate = "2017-12-05 07:00:00.0"
return {
"returnCode": 1,
"lastLoginDate": lastLoginDate,
"loginCount": loginCt,
"consecutiveLoginCount": 0, # We don't really have a way to track this...
"loginId": loginCt, # Used with the playlog!
}
def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
playlog = data["userPlaylog"]
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:
user_id = data["userId"]
upsert = data["upsertUserAll"]
if "userData" in upsert and len(upsert["userData"]) > 0:
upsert["userData"][0]["isNetMember"] = 1
upsert["userData"][0].pop("accessCode")
self.data.profile.put_profile_detail(
user_id, self.version, upsert["userData"][0]
)
if "userExtend" in upsert and len(upsert["userExtend"]) > 0:
self.data.profile.put_profile_extend(
user_id, self.version, upsert["userExtend"][0]
)
if "userGhost" in upsert:
for ghost in upsert["userGhost"]:
self.data.profile.put_profile_extend(user_id, self.version, ghost)
if "userOption" in upsert and len(upsert["userOption"]) > 0:
self.data.profile.put_profile_option(
user_id, self.version, upsert["userOption"][0]
)
if "userRatingList" in upsert and len(upsert["userRatingList"]) > 0:
self.data.profile.put_profile_rating(
user_id, self.version, upsert["userRatingList"][0]
)
if "userActivityList" in upsert and len(upsert["userActivityList"]) > 0:
for k, v in upsert["userActivityList"][0].items():
for act in v:
self.data.profile.put_profile_activity(user_id, act)
if "userChargeList" in upsert and len(upsert["userChargeList"]) > 0:
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(
user_id,
charge["chargeId"],
charge["stock"],
datetime.strptime(
charge["purchaseDate"], Mai2Constants.DATE_TIME_FORMAT
),
datetime.strptime(
charge["validDate"], Mai2Constants.DATE_TIME_FORMAT
),
)
if "userCharacterList" in upsert and len(upsert["userCharacterList"]) > 0:
for char in upsert["userCharacterList"]:
self.data.item.put_character(
user_id,
char["characterId"],
char["level"],
char["awakening"],
char["useCount"],
)
if "userItemList" in upsert and len(upsert["userItemList"]) > 0:
for item in upsert["userItemList"]:
self.data.item.put_item(
user_id,
int(item["itemKind"]),
item["itemId"],
item["stock"],
item["isValid"],
)
if "userLoginBonusList" in upsert and len(upsert["userLoginBonusList"]) > 0:
for login_bonus in upsert["userLoginBonusList"]:
self.data.item.put_login_bonus(
user_id,
login_bonus["bonusId"],
login_bonus["point"],
login_bonus["isCurrent"],
login_bonus["isComplete"],
)
if "userMapList" in upsert and len(upsert["userMapList"]) > 0:
for map in upsert["userMapList"]:
self.data.item.put_map(
user_id,
map["mapId"],
map["distance"],
map["isLock"],
map["isClear"],
map["isComplete"],
)
if "userMusicDetailList" in upsert and len(upsert["userMusicDetailList"]) > 0:
for music in upsert["userMusicDetailList"]:
self.data.score.put_best_score(user_id, music)
if "userCourseList" in upsert and len(upsert["userCourseList"]) > 0:
for course in upsert["userCourseList"]:
self.data.score.put_course(user_id, course)
if "userFavoriteList" in upsert and len(upsert["userFavoriteList"]) > 0:
for fav in upsert["userFavoriteList"]:
self.data.item.put_favorite(user_id, fav["kind"], fav["itemIdList"])
if (
"userFriendSeasonRankingList" in upsert
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:
return {"returnCode": 1}
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
if profile is None:
return
profile_dict = profile._asdict()
profile_dict.pop("id")
profile_dict.pop("user")
profile_dict.pop("version")
return {"userId": data["userId"], "userData": profile_dict}
def handle_get_user_extend_api_request(self, data: Dict) -> Dict:
extend = self.data.profile.get_profile_extend(data["userId"], self.version)
if extend is None:
return
extend_dict = extend._asdict()
extend_dict.pop("id")
extend_dict.pop("user")
extend_dict.pop("version")
return {"userId": data["userId"], "userExtend": extend_dict}
def handle_get_user_option_api_request(self, data: Dict) -> Dict:
options = self.data.profile.get_profile_option(data["userId"], self.version)
if options is None:
return
options_dict = options._asdict()
options_dict.pop("id")
options_dict.pop("user")
options_dict.pop("version")
return {"userId": data["userId"], "userOption": options_dict}
def handle_get_user_card_api_request(self, data: Dict) -> Dict:
user_cards = self.data.item.get_cards(data["userId"])
if user_cards is None:
return {"userId": data["userId"], "nextIndex": 0, "userCardList": []}
max_ct = data["maxCount"]
next_idx = data["nextIndex"]
start_idx = next_idx
end_idx = max_ct + start_idx
if len(user_cards[start_idx:]) > max_ct:
next_idx += max_ct
else:
next_idx = 0
card_list = []
for card in user_cards:
tmp = card._asdict()
tmp.pop("id")
tmp.pop("user")
tmp["startDate"] = datetime.strftime(
tmp["startDate"], Mai2Constants.DATE_TIME_FORMAT
)
tmp["endDate"] = datetime.strftime(
tmp["endDate"], Mai2Constants.DATE_TIME_FORMAT
)
card_list.append(tmp)
return {
"userId": data["userId"],
"nextIndex": next_idx,
"userCardList": card_list[start_idx:end_idx],
}
def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
user_charges = self.data.item.get_charges(data["userId"])
if user_charges is None:
return {"userId": data["userId"], "length": 0, "userChargeList": []}
user_charge_list = []
for charge in user_charges:
tmp = charge._asdict()
tmp.pop("id")
tmp.pop("user")
tmp["purchaseDate"] = datetime.strftime(
tmp["purchaseDate"], Mai2Constants.DATE_TIME_FORMAT
)
tmp["validDate"] = datetime.strftime(
tmp["validDate"], Mai2Constants.DATE_TIME_FORMAT
)
user_charge_list.append(tmp)
return {
"userId": data["userId"],
"length": len(user_charge_list),
"userChargeList": user_charge_list,
}
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
kind = int(data["nextIndex"] / 10000000000)
next_idx = int(data["nextIndex"] % 10000000000)
user_item_list = self.data.item.get_items(data["userId"], kind)
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
else:
next_idx = xout
return {
"userId": data["userId"],
"nextIndex": next_idx,
"itemKind": kind,
"userItemList": items,
}
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
characters = self.data.item.get_characters(data["userId"])
chara_list = []
for chara in characters:
tmp = chara._asdict()
tmp.pop("id")
tmp.pop("user")
chara_list.append(tmp)
return {"userId": data["userId"], "userCharacterList": chara_list}
def handle_get_user_favorite_api_request(self, data: Dict) -> Dict:
favorites = self.data.item.get_favorites(data["userId"], data["itemKind"])
if favorites is None:
return
userFavs = []
for fav in favorites:
userFavs.append(
{
"userId": data["userId"],
"itemKind": fav["itemKind"],
"itemIdList": fav["itemIdList"],
}
)
return {"userId": data["userId"], "userFavoriteData": userFavs}
def handle_get_user_ghost_api_request(self, data: Dict) -> Dict:
ghost = self.data.profile.get_profile_ghost(data["userId"], self.version)
if ghost is None:
return
ghost_dict = ghost._asdict()
ghost_dict.pop("user")
ghost_dict.pop("id")
ghost_dict.pop("version_int")
return {"userId": data["userId"], "userGhost": ghost_dict}
def handle_get_user_rating_api_request(self, data: Dict) -> Dict:
rating = self.data.profile.get_profile_rating(data["userId"], self.version)
if rating is None:
return
rating_dict = rating._asdict()
rating_dict.pop("user")
rating_dict.pop("id")
rating_dict.pop("version")
return {"userId": data["userId"], "userRating": rating_dict}
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
"""
kind 1 is playlist, kind 2 is music list
"""
playlist = self.data.profile.get_profile_activity(data["userId"], 1)
musiclist = self.data.profile.get_profile_activity(data["userId"], 2)
if playlist is None or musiclist is None:
return
plst = []
mlst = []
for play in playlist:
tmp = play._asdict()
tmp["id"] = tmp["activityId"]
tmp.pop("activityId")
tmp.pop("user")
plst.append(tmp)
for music in musiclist:
tmp = music._asdict()
tmp["id"] = tmp["activityId"]
tmp.pop("activityId")
tmp.pop("user")
mlst.append(tmp)
return {"userActivity": {"playList": plst, "musicList": mlst}}
def handle_get_user_course_api_request(self, data: Dict) -> Dict:
user_courses = self.data.score.get_courses(data["userId"])
if user_courses is None:
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []}
course_list = []
for course in user_courses:
tmp = course._asdict()
tmp.pop("user")
tmp.pop("id")
course_list.append(tmp)
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": course_list}
def handle_get_user_portrait_api_request(self, data: Dict) -> Dict:
# No support for custom pfps
return {"length": 0, "userPortraitList": []}
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"])
if friend_season_ranking is None:
return {
"userId": data["userId"],
"nextIndex": 0,
"userFriendSeasonRankingList": [],
}
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"
)
friend_season_ranking_list.append(tmp)
if len(friend_season_ranking_list) >= max_ct:
break
if len(friend_season_ranking) >= next_idx + max_ct:
next_idx += max_ct
else:
next_idx = 0
return {
"userId": data["userId"],
"nextIndex": next_idx,
"userFriendSeasonRankingList": friend_season_ranking_list,
}
def handle_get_user_map_api_request(self, data: Dict) -> Dict:
maps = self.data.item.get_maps(data["userId"])
if maps is None:
return {
"userId": data["userId"],
"nextIndex": 0,
"userMapList": [],
}
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
if len(maps) >= next_idx + max_ct:
next_idx += max_ct
else:
next_idx = 0
return {
"userId": data["userId"],
"nextIndex": next_idx,
"userMapList": map_list,
}
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
login_bonuses = self.data.item.get_login_bonuses(data["userId"])
if login_bonuses is None:
return {
"userId": data["userId"],
"nextIndex": 0,
"userLoginBonusList": [],
}
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
if len(login_bonuses) >= next_idx + max_ct:
next_idx += max_ct
else:
next_idx = 0
return {
"userId": data["userId"],
"nextIndex": next_idx,
"userLoginBonusList": login_bonus_list,
}
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
return {"userId": data["userId"], "length": 0, "userRegionList": []}
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
songs = self.data.score.get_best_scores(data["userId"])
music_detail_list = []
next_index = 0
if songs is not None:
for song in songs:
tmp = song._asdict()
tmp.pop("id")
tmp.pop("user")
music_detail_list.append(tmp)
if len(music_detail_list) == data["maxCount"]:
next_index = data["maxCount"] + data["nextIndex"]
break
return {
"userId": data["userId"],
"nextIndex": next_index,
"userMusicList": [{"userMusicDetailList": music_detail_list}],
}
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_detail(data["userId"], self.version)
if p is None:
return {}
return {
"userName": p["userName"],
"rating": p["playerRating"],
# hardcode lastDataVersion for CardMaker 1.34
"lastDataVersion": "1.20.00",
"isLogin": False,
"isExistSellingCard": False,
}
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
# user already exists, because the preview checks that already
p = self.data.profile.get_profile_detail(data["userId"], self.version)
cards = self.data.card.get_user_cards(data["userId"])
if cards is None or len(cards) == 0:
# This should never happen
self.logger.error(
f"handle_get_user_data_api_request: Internal error - No cards found for user id {data['userId']}"
)
return {}
# get the dict representation of the row so we can modify values
user_data = p._asdict()
# remove the values the game doesn't want
user_data.pop("id")
user_data.pop("user")
user_data.pop("version")
return {"userId": data["userId"], "userData": user_data}
def handle_cm_login_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}
def handle_cm_logout_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}
def handle_cm_get_selling_card_api_request(self, data: Dict) -> Dict:
selling_cards = self.data.static.get_enabled_cards(self.version)
if selling_cards is None:
return {"length": 0, "sellingCardList": []}
selling_card_list = []
for card in selling_cards:
tmp = card._asdict()
tmp.pop("id")
tmp.pop("version")
tmp.pop("cardName")
tmp.pop("enabled")
tmp["startDate"] = datetime.strftime(tmp["startDate"], "%Y-%m-%d %H:%M:%S")
tmp["endDate"] = datetime.strftime(tmp["endDate"], "%Y-%m-%d %H:%M:%S")
tmp["noticeStartDate"] = datetime.strftime(
tmp["noticeStartDate"], "%Y-%m-%d %H:%M:%S"
)
tmp["noticeEndDate"] = datetime.strftime(
tmp["noticeEndDate"], "%Y-%m-%d %H:%M:%S"
)
selling_card_list.append(tmp)
return {"length": len(selling_card_list), "sellingCardList": selling_card_list}
def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict:
user_cards = self.data.item.get_cards(data["userId"])
if user_cards is None:
return {"returnCode": 1, "length": 0, "nextIndex": 0, "userCardList": []}
max_ct = data["maxCount"]
next_idx = data["nextIndex"]
start_idx = next_idx
end_idx = max_ct + start_idx
if len(user_cards[start_idx:]) > max_ct:
next_idx += max_ct
else:
next_idx = 0
card_list = []
for card in user_cards:
tmp = card._asdict()
tmp.pop("id")
tmp.pop("user")
tmp["startDate"] = datetime.strftime(tmp["startDate"], "%Y-%m-%d %H:%M:%S")
tmp["endDate"] = datetime.strftime(tmp["endDate"], "%Y-%m-%d %H:%M:%S")
card_list.append(tmp)
return {
"returnCode": 1,
"length": len(card_list[start_idx:end_idx]),
"nextIndex": next_idx,
"userCardList": card_list[start_idx:end_idx],
}
def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
super().handle_get_user_item_api_request(data)
def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
characters = self.data.item.get_characters(data["userId"])
chara_list = []
for chara in characters:
chara_list.append(
{
"characterId": chara["characterId"],
# no clue why those values are even needed
"point": 0,
"count": 0,
"level": chara["level"],
"nextAwake": 0,
"nextAwakePercent": 0,
"favorite": False,
"awakening": chara["awakening"],
"useCount": chara["useCount"],
}
)
return {
"returnCode": 1,
"length": len(chara_list),
"userCharacterList": chara_list,
}
def handle_cm_get_user_card_print_error_api_request(self, data: Dict) -> Dict:
return {"length": 0, "userPrintDetailList": []}
def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
upsert = data["userPrintDetail"]
# set a random card serial number
serial_id = "".join([str(randint(0, 9)) for _ in range(20)])
user_card = upsert["userCard"]
self.data.item.put_card(
user_id,
user_card["cardId"],
user_card["cardTypeId"],
user_card["charaId"],
user_card["mapId"],
)
# properly format userPrintDetail for the database
upsert.pop("userCard")
upsert.pop("serialId")
upsert["printDate"] = datetime.strptime(upsert["printDate"], "%Y-%m-%d")
self.data.item.put_user_print_detail(user_id, serial_id, upsert)
return {
"returnCode": 1,
"orderId": 0,
"serialId": serial_id,
"startDate": "2018-01-01 00:00:00",
"endDate": "2038-01-01 00:00:00",
}
def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
return {
"returnCode": 1,
"orderId": 0,
"serialId": data["userPrintlog"]["serialId"],
}
def handle_cm_upsert_buy_card_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}

View File

@ -4,12 +4,12 @@ import pytz
import json import json
from core.config import CoreConfig from core.config import CoreConfig
from titles.mai2.base import Mai2Base from titles.mai2.dx import Mai2DX
from titles.mai2.config import Mai2Config from titles.mai2.config import Mai2Config
from titles.mai2.const import Mai2Constants from titles.mai2.const import Mai2Constants
class Mai2DXPlus(Mai2Base): class Mai2DXPlus(Mai2DX):
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None: def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
super().__init__(cfg, game_cfg) super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_PLUS self.version = Mai2Constants.VER_MAIMAI_DX_PLUS

View File

@ -1,12 +1,12 @@
from typing import Dict from typing import Dict
from core.config import CoreConfig from core.config import CoreConfig
from titles.mai2.universeplus import Mai2UniversePlus from titles.mai2.dx import Mai2DX
from titles.mai2.const import Mai2Constants from titles.mai2.const import Mai2Constants
from titles.mai2.config import Mai2Config from titles.mai2.config import Mai2Config
class Mai2Festival(Mai2UniversePlus): class Mai2Festival(Mai2DX):
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None: def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
super().__init__(cfg, game_cfg) super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL

View File

@ -34,16 +34,6 @@ class Mai2Servlet:
) )
self.versions = [ self.versions = [
Mai2DX,
Mai2DXPlus,
Mai2Splash,
Mai2SplashPlus,
Mai2Universe,
Mai2UniversePlus,
Mai2Festival,
]
self.versions_old = [
Mai2Base, Mai2Base,
None, None,
None, None,
@ -57,6 +47,13 @@ class Mai2Servlet:
None, None,
None, None,
Mai2Finale, Mai2Finale,
Mai2DX,
Mai2DXPlus,
Mai2Splash,
Mai2SplashPlus,
Mai2Universe,
Mai2UniversePlus,
Mai2Festival,
] ]
self.logger = logging.getLogger("mai2") self.logger = logging.getLogger("mai2")
@ -122,7 +119,7 @@ class Mai2Servlet:
endpoint = url_split[len(url_split) - 1] endpoint = url_split[len(url_split) - 1]
client_ip = Utils.get_ip_addr(request) client_ip = Utils.get_ip_addr(request)
if request.uri.startswith(b"/SDEY"): if request.uri.startswith(b"/SDEZ"):
if version < 105: # 1.0 if version < 105: # 1.0
internal_ver = Mai2Constants.VER_MAIMAI_DX internal_ver = Mai2Constants.VER_MAIMAI_DX
elif version >= 105 and version < 110: # Plus elif version >= 105 and version < 110: # Plus
@ -187,12 +184,7 @@ class Mai2Servlet:
self.logger.debug(req_data) 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 internal_ver >= Mai2Constants.VER_MAIMAI:
handler_cls = self.versions_old[internal_ver](self.core_cfg, self.game_cfg)
else:
handler_cls = self.versions[internal_ver](self.core_cfg, self.game_cfg)
if not hasattr(handler_cls, 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}")
@ -213,3 +205,9 @@ class Mai2Servlet:
self.logger.debug(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"))
def render_GET(self, request: Request, version: int, url_path: str) -> bytes:
if url_path.endswith("ping"):
return zlib.compress(b"ok")
else:
return zlib.compress(b"{}")

View File

@ -4,12 +4,12 @@ import pytz
import json import json
from core.config import CoreConfig from core.config import CoreConfig
from titles.mai2.base import Mai2Base from titles.mai2.dx import Mai2DX
from titles.mai2.config import Mai2Config from titles.mai2.config import Mai2Config
from titles.mai2.const import Mai2Constants from titles.mai2.const import Mai2Constants
class Mai2Splash(Mai2Base): class Mai2Splash(Mai2DX):
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None: def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
super().__init__(cfg, game_cfg) super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_SPLASH self.version = Mai2Constants.VER_MAIMAI_DX_SPLASH

View File

@ -4,12 +4,12 @@ import pytz
import json import json
from core.config import CoreConfig from core.config import CoreConfig
from titles.mai2.base import Mai2Base from titles.mai2.dx import Mai2DX
from titles.mai2.config import Mai2Config from titles.mai2.config import Mai2Config
from titles.mai2.const import Mai2Constants from titles.mai2.const import Mai2Constants
class Mai2SplashPlus(Mai2Base): class Mai2SplashPlus(Mai2DX):
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None: def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
super().__init__(cfg, game_cfg) super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_SPLASH_PLUS self.version = Mai2Constants.VER_MAIMAI_DX_SPLASH_PLUS

View File

@ -5,185 +5,12 @@ import pytz
import json import json
from core.config import CoreConfig from core.config import CoreConfig
from titles.mai2.base import Mai2Base from titles.mai2.dx import Mai2DX
from titles.mai2.const import Mai2Constants from titles.mai2.const import Mai2Constants
from titles.mai2.config import Mai2Config from titles.mai2.config import Mai2Config
class Mai2Universe(Mai2Base): class Mai2Universe(Mai2DX):
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None: def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
super().__init__(cfg, game_cfg) super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_detail(data["userId"], self.version)
if p is None:
return {}
return {
"userName": p["userName"],
"rating": p["playerRating"],
# hardcode lastDataVersion for CardMaker 1.34
"lastDataVersion": "1.20.00",
"isLogin": False,
"isExistSellingCard": False,
}
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
# user already exists, because the preview checks that already
p = self.data.profile.get_profile_detail(data["userId"], self.version)
cards = self.data.card.get_user_cards(data["userId"])
if cards is None or len(cards) == 0:
# This should never happen
self.logger.error(
f"handle_get_user_data_api_request: Internal error - No cards found for user id {data['userId']}"
)
return {}
# get the dict representation of the row so we can modify values
user_data = p._asdict()
# remove the values the game doesn't want
user_data.pop("id")
user_data.pop("user")
user_data.pop("version")
return {"userId": data["userId"], "userData": user_data}
def handle_cm_login_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}
def handle_cm_logout_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}
def handle_cm_get_selling_card_api_request(self, data: Dict) -> Dict:
selling_cards = self.data.static.get_enabled_cards(self.version)
if selling_cards is None:
return {"length": 0, "sellingCardList": []}
selling_card_list = []
for card in selling_cards:
tmp = card._asdict()
tmp.pop("id")
tmp.pop("version")
tmp.pop("cardName")
tmp.pop("enabled")
tmp["startDate"] = datetime.strftime(tmp["startDate"], "%Y-%m-%d %H:%M:%S")
tmp["endDate"] = datetime.strftime(tmp["endDate"], "%Y-%m-%d %H:%M:%S")
tmp["noticeStartDate"] = datetime.strftime(
tmp["noticeStartDate"], "%Y-%m-%d %H:%M:%S"
)
tmp["noticeEndDate"] = datetime.strftime(
tmp["noticeEndDate"], "%Y-%m-%d %H:%M:%S"
)
selling_card_list.append(tmp)
return {"length": len(selling_card_list), "sellingCardList": selling_card_list}
def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict:
user_cards = self.data.item.get_cards(data["userId"])
if user_cards is None:
return {"returnCode": 1, "length": 0, "nextIndex": 0, "userCardList": []}
max_ct = data["maxCount"]
next_idx = data["nextIndex"]
start_idx = next_idx
end_idx = max_ct + start_idx
if len(user_cards[start_idx:]) > max_ct:
next_idx += max_ct
else:
next_idx = 0
card_list = []
for card in user_cards:
tmp = card._asdict()
tmp.pop("id")
tmp.pop("user")
tmp["startDate"] = datetime.strftime(tmp["startDate"], "%Y-%m-%d %H:%M:%S")
tmp["endDate"] = datetime.strftime(tmp["endDate"], "%Y-%m-%d %H:%M:%S")
card_list.append(tmp)
return {
"returnCode": 1,
"length": len(card_list[start_idx:end_idx]),
"nextIndex": next_idx,
"userCardList": card_list[start_idx:end_idx],
}
def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
super().handle_get_user_item_api_request(data)
def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
characters = self.data.item.get_characters(data["userId"])
chara_list = []
for chara in characters:
chara_list.append(
{
"characterId": chara["characterId"],
# no clue why those values are even needed
"point": 0,
"count": 0,
"level": chara["level"],
"nextAwake": 0,
"nextAwakePercent": 0,
"favorite": False,
"awakening": chara["awakening"],
"useCount": chara["useCount"],
}
)
return {
"returnCode": 1,
"length": len(chara_list),
"userCharacterList": chara_list,
}
def handle_cm_get_user_card_print_error_api_request(self, data: Dict) -> Dict:
return {"length": 0, "userPrintDetailList": []}
def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
upsert = data["userPrintDetail"]
# set a random card serial number
serial_id = "".join([str(randint(0, 9)) for _ in range(20)])
user_card = upsert["userCard"]
self.data.item.put_card(
user_id,
user_card["cardId"],
user_card["cardTypeId"],
user_card["charaId"],
user_card["mapId"],
)
# properly format userPrintDetail for the database
upsert.pop("userCard")
upsert.pop("serialId")
upsert["printDate"] = datetime.strptime(upsert["printDate"], "%Y-%m-%d")
self.data.item.put_user_print_detail(user_id, serial_id, upsert)
return {
"returnCode": 1,
"orderId": 0,
"serialId": serial_id,
"startDate": "2018-01-01 00:00:00",
"endDate": "2038-01-01 00:00:00",
}
def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
return {
"returnCode": 1,
"orderId": 0,
"serialId": data["userPrintlog"]["serialId"],
}
def handle_cm_upsert_buy_card_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}

View File

@ -1,12 +1,12 @@
from typing import Dict from typing import Dict
from core.config import CoreConfig from core.config import CoreConfig
from titles.mai2.universe import Mai2Universe from titles.mai2.dx import Mai2DX
from titles.mai2.const import Mai2Constants from titles.mai2.const import Mai2Constants
from titles.mai2.config import Mai2Config from titles.mai2.config import Mai2Config
class Mai2UniversePlus(Mai2Universe): class Mai2UniversePlus(Mai2DX):
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None: def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
super().__init__(cfg, game_cfg) super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS