mirror of
https://gitea.tendokyu.moe/Hay1tsme/artemis.git
synced 2026-02-15 20:27:29 +08:00
Merge branch 'develop' into diva_handler_classes
This commit is contained in:
@@ -6,13 +6,5 @@ from titles.chuni.read import ChuniReader
|
||||
index = ChuniServlet
|
||||
database = ChuniData
|
||||
reader = ChuniReader
|
||||
|
||||
use_default_title = True
|
||||
include_protocol = True
|
||||
title_secure = False
|
||||
game_codes = [ChuniConstants.GAME_CODE, ChuniConstants.GAME_CODE_NEW]
|
||||
trailing_slash = True
|
||||
use_default_host = False
|
||||
host = ""
|
||||
|
||||
current_schema_version = 1
|
||||
|
||||
@@ -5,12 +5,13 @@ from titles.chuni.base import ChuniBase
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniAir(ChuniBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_AIR
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.10.00"
|
||||
return ret
|
||||
return ret
|
||||
|
||||
@@ -5,12 +5,13 @@ from titles.chuni.base import ChuniBase
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniAirPlus(ChuniBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_AIR_PLUS
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.15.00"
|
||||
return ret
|
||||
return ret
|
||||
|
||||
@@ -7,12 +7,13 @@ from titles.chuni.base import ChuniBase
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniAmazon(ChuniBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_AMAZON
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.30.00"
|
||||
return ret
|
||||
|
||||
@@ -7,12 +7,13 @@ from titles.chuni.base import ChuniBase
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniAmazonPlus(ChuniBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_AMAZON_PLUS
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
||||
return ret
|
||||
|
||||
@@ -11,7 +11,8 @@ from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.database import ChuniData
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
class ChuniBase():
|
||||
|
||||
class ChuniBase:
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.game_cfg = game_cfg
|
||||
@@ -20,34 +21,33 @@ class ChuniBase():
|
||||
self.logger = logging.getLogger("chuni")
|
||||
self.game = ChuniConstants.GAME_CODE
|
||||
self.version = ChuniConstants.VER_CHUNITHM
|
||||
|
||||
|
||||
def handle_game_login_api_request(self, data: Dict) -> Dict:
|
||||
#self.data.base.log_event("chuni", "login", logging.INFO, {"version": self.version, "user": data["userId"]})
|
||||
return { "returnCode": 1 }
|
||||
|
||||
# self.data.base.log_event("chuni", "login", logging.INFO, {"version": self.version, "user": data["userId"]})
|
||||
return {"returnCode": 1}
|
||||
|
||||
def handle_game_logout_api_request(self, data: Dict) -> Dict:
|
||||
#self.data.base.log_event("chuni", "logout", logging.INFO, {"version": self.version, "user": data["userId"]})
|
||||
return { "returnCode": 1 }
|
||||
# self.data.base.log_event("chuni", "logout", logging.INFO, {"version": self.version, "user": data["userId"]})
|
||||
return {"returnCode": 1}
|
||||
|
||||
def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
|
||||
game_charge_list = self.data.static.get_enabled_charges(self.version)
|
||||
|
||||
charges = []
|
||||
for x in range(len(game_charge_list)):
|
||||
charges.append({
|
||||
"orderId": x,
|
||||
"chargeId": game_charge_list[x]["chargeId"],
|
||||
"price": 1,
|
||||
"startDate": "2017-12-05 07:00:00.0",
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
"salePrice": 1,
|
||||
"saleStartDate": "2017-12-05 07:00:00.0",
|
||||
"saleEndDate": "2099-12-31 00:00:00.0"
|
||||
})
|
||||
return {
|
||||
"length": len(charges),
|
||||
"gameChargeList": charges
|
||||
}
|
||||
for x in range(len(game_charge_list)):
|
||||
charges.append(
|
||||
{
|
||||
"orderId": x,
|
||||
"chargeId": game_charge_list[x]["chargeId"],
|
||||
"price": 1,
|
||||
"startDate": "2017-12-05 07:00:00.0",
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
"salePrice": 1,
|
||||
"saleStartDate": "2017-12-05 07:00:00.0",
|
||||
"saleEndDate": "2099-12-31 00:00:00.0",
|
||||
}
|
||||
)
|
||||
return {"length": len(charges), "gameChargeList": charges}
|
||||
|
||||
def handle_get_game_event_api_request(self, data: Dict) -> Dict:
|
||||
game_events = self.data.static.get_enabled_events(self.version)
|
||||
@@ -62,26 +62,30 @@ class ChuniBase():
|
||||
event_list.append(tmp)
|
||||
|
||||
return {
|
||||
"type": data["type"],
|
||||
"length": len(event_list),
|
||||
"gameEventList": event_list
|
||||
"type": data["type"],
|
||||
"length": len(event_list),
|
||||
"gameEventList": event_list,
|
||||
}
|
||||
|
||||
def handle_get_game_idlist_api_request(self, data: Dict) -> Dict:
|
||||
return { "type": data["type"], "length": 0, "gameIdlistList": [] }
|
||||
return {"type": data["type"], "length": 0, "gameIdlistList": []}
|
||||
|
||||
def handle_get_game_message_api_request(self, data: Dict) -> Dict:
|
||||
return { "type": data["type"], "length": "0", "gameMessageList": [] }
|
||||
return {"type": data["type"], "length": "0", "gameMessageList": []}
|
||||
|
||||
def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
|
||||
return { "type": data["type"], "gameRankingList": [] }
|
||||
return {"type": data["type"], "gameRankingList": []}
|
||||
|
||||
def handle_get_game_sale_api_request(self, data: Dict) -> Dict:
|
||||
return { "type": data["type"], "length": 0, "gameSaleList": [] }
|
||||
return {"type": data["type"], "length": 0, "gameSaleList": []}
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
reboot_start = datetime.strftime(datetime.now() - timedelta(hours=4), self.date_time_format)
|
||||
reboot_end = datetime.strftime(datetime.now() - timedelta(hours=3), self.date_time_format)
|
||||
reboot_start = datetime.strftime(
|
||||
datetime.now() - timedelta(hours=4), self.date_time_format
|
||||
)
|
||||
reboot_end = datetime.strftime(
|
||||
datetime.now() - timedelta(hours=3), self.date_time_format
|
||||
)
|
||||
return {
|
||||
"gameSetting": {
|
||||
"dataVersion": "1.00.00",
|
||||
@@ -94,15 +98,17 @@ class ChuniBase():
|
||||
"maxCountItem": 300,
|
||||
"maxCountMusic": 300,
|
||||
},
|
||||
"isDumpUpload": "false",
|
||||
"isAou": "false",
|
||||
"isDumpUpload": "false",
|
||||
"isAou": "false",
|
||||
}
|
||||
|
||||
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
||||
user_activity_list = self.data.profile.get_profile_activity(data["userId"], data["kind"])
|
||||
|
||||
user_activity_list = self.data.profile.get_profile_activity(
|
||||
data["userId"], data["kind"]
|
||||
)
|
||||
|
||||
activity_list = []
|
||||
|
||||
|
||||
for activity in user_activity_list:
|
||||
tmp = activity._asdict()
|
||||
tmp.pop("user")
|
||||
@@ -111,15 +117,16 @@ class ChuniBase():
|
||||
activity_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(activity_list),
|
||||
"kind": data["kind"],
|
||||
"userActivityList": activity_list
|
||||
"userId": data["userId"],
|
||||
"length": len(activity_list),
|
||||
"kind": data["kind"],
|
||||
"userActivityList": activity_list,
|
||||
}
|
||||
|
||||
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
|
||||
characters = self.data.item.get_characters(data["userId"])
|
||||
if characters is None: return {}
|
||||
if characters is None:
|
||||
return {}
|
||||
next_idx = -1
|
||||
|
||||
characterList = []
|
||||
@@ -131,15 +138,17 @@ class ChuniBase():
|
||||
|
||||
if len(characterList) >= int(data["maxCount"]):
|
||||
break
|
||||
|
||||
if len(characterList) >= int(data["maxCount"]) and len(characters) > int(data["maxCount"]) + int(data["nextIndex"]):
|
||||
|
||||
if len(characterList) >= int(data["maxCount"]) and len(characters) > int(
|
||||
data["maxCount"]
|
||||
) + int(data["nextIndex"]):
|
||||
next_idx = int(data["maxCount"]) + int(data["nextIndex"]) + 1
|
||||
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userId": data["userId"],
|
||||
"length": len(characterList),
|
||||
"nextIndex": next_idx,
|
||||
"userCharacterList": characterList
|
||||
"nextIndex": next_idx,
|
||||
"userCharacterList": characterList,
|
||||
}
|
||||
|
||||
def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
|
||||
@@ -153,21 +162,21 @@ class ChuniBase():
|
||||
charge_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userId": data["userId"],
|
||||
"length": len(charge_list),
|
||||
"userChargeList": charge_list
|
||||
"userChargeList": charge_list,
|
||||
}
|
||||
|
||||
def handle_get_user_course_api_request(self, data: Dict) -> Dict:
|
||||
user_course_list = self.data.score.get_courses(data["userId"])
|
||||
if user_course_list is None:
|
||||
if user_course_list is None:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userId": data["userId"],
|
||||
"length": 0,
|
||||
"nextIndex": -1,
|
||||
"userCourseList": []
|
||||
"nextIndex": -1,
|
||||
"userCourseList": [],
|
||||
}
|
||||
|
||||
|
||||
course_list = []
|
||||
next_idx = int(data["nextIndex"])
|
||||
max_ct = int(data["maxCount"])
|
||||
@@ -180,51 +189,48 @@ class ChuniBase():
|
||||
|
||||
if len(user_course_list) >= max_ct:
|
||||
break
|
||||
|
||||
|
||||
if len(user_course_list) >= max_ct:
|
||||
next_idx = next_idx + max_ct
|
||||
else:
|
||||
next_idx = -1
|
||||
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userId": data["userId"],
|
||||
"length": len(course_list),
|
||||
"nextIndex": next_idx,
|
||||
"userCourseList": course_list
|
||||
"nextIndex": next_idx,
|
||||
"userCourseList": course_list,
|
||||
}
|
||||
|
||||
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||
p = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||
if p is None: return {}
|
||||
if p is None:
|
||||
return {}
|
||||
|
||||
profile = p._asdict()
|
||||
profile.pop("id")
|
||||
profile.pop("user")
|
||||
profile.pop("version")
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userData": profile
|
||||
}
|
||||
return {"userId": data["userId"], "userData": profile}
|
||||
|
||||
def handle_get_user_data_ex_api_request(self, data: Dict) -> Dict:
|
||||
p = self.data.profile.get_profile_data_ex(data["userId"], self.version)
|
||||
if p is None: return {}
|
||||
if p is None:
|
||||
return {}
|
||||
|
||||
profile = p._asdict()
|
||||
profile.pop("id")
|
||||
profile.pop("user")
|
||||
profile.pop("version")
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userDataEx": profile
|
||||
}
|
||||
return {"userId": data["userId"], "userDataEx": profile}
|
||||
|
||||
def handle_get_user_duel_api_request(self, data: Dict) -> Dict:
|
||||
user_duel_list = self.data.item.get_duels(data["userId"])
|
||||
if user_duel_list is None: return {}
|
||||
|
||||
if user_duel_list is None:
|
||||
return {}
|
||||
|
||||
duel_list = []
|
||||
for duel in user_duel_list:
|
||||
tmp = duel._asdict()
|
||||
@@ -233,18 +239,18 @@ class ChuniBase():
|
||||
duel_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userId": data["userId"],
|
||||
"length": len(duel_list),
|
||||
"userDuelList": duel_list
|
||||
"userDuelList": duel_list,
|
||||
}
|
||||
|
||||
def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userId": data["userId"],
|
||||
"length": 0,
|
||||
"kind": data["kind"],
|
||||
"nextIndex": -1,
|
||||
"userFavoriteItemList": []
|
||||
"kind": data["kind"],
|
||||
"nextIndex": -1,
|
||||
"userFavoriteItemList": [],
|
||||
}
|
||||
|
||||
def handle_get_user_favorite_music_api_request(self, data: Dict) -> Dict:
|
||||
@@ -252,22 +258,23 @@ class ChuniBase():
|
||||
This is handled via the webui, which we don't have right now
|
||||
"""
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": 0,
|
||||
"userFavoriteMusicList": []
|
||||
}
|
||||
return {"userId": data["userId"], "length": 0, "userFavoriteMusicList": []}
|
||||
|
||||
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||
kind = int(int(data["nextIndex"]) / 10000000000)
|
||||
next_idx = int(int(data["nextIndex"]) % 10000000000)
|
||||
user_item_list = self.data.item.get_items(data["userId"], kind)
|
||||
|
||||
if user_item_list is None or len(user_item_list) == 0:
|
||||
return {"userId": data["userId"], "nextIndex": -1, "itemKind": kind, "userItemList": []}
|
||||
if user_item_list is None or len(user_item_list) == 0:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"nextIndex": -1,
|
||||
"itemKind": kind,
|
||||
"userItemList": [],
|
||||
}
|
||||
|
||||
items: list[Dict[str, Any]] = []
|
||||
for i in range(next_idx, len(user_item_list)):
|
||||
for i in range(next_idx, len(user_item_list)):
|
||||
tmp = user_item_list[i]._asdict()
|
||||
tmp.pop("user")
|
||||
tmp.pop("id")
|
||||
@@ -277,38 +284,47 @@ class ChuniBase():
|
||||
|
||||
xout = kind * 10000000000 + next_idx + len(items)
|
||||
|
||||
if len(items) < int(data["maxCount"]): nextIndex = 0
|
||||
else: nextIndex = xout
|
||||
if len(items) < int(data["maxCount"]):
|
||||
nextIndex = 0
|
||||
else:
|
||||
nextIndex = xout
|
||||
|
||||
return {"userId": data["userId"], "nextIndex": nextIndex, "itemKind": kind, "length": len(items), "userItemList": items}
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"nextIndex": nextIndex,
|
||||
"itemKind": kind,
|
||||
"length": len(items),
|
||||
"userItemList": items,
|
||||
}
|
||||
|
||||
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
Unsure how to get this to trigger...
|
||||
"""
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userId": data["userId"],
|
||||
"length": 2,
|
||||
"userLoginBonusList": [
|
||||
{
|
||||
"presetId": '10',
|
||||
"bonusCount": '0',
|
||||
"lastUpdateDate": "1970-01-01 09:00:00",
|
||||
"isWatched": "true"
|
||||
"presetId": "10",
|
||||
"bonusCount": "0",
|
||||
"lastUpdateDate": "1970-01-01 09:00:00",
|
||||
"isWatched": "true",
|
||||
},
|
||||
{
|
||||
"presetId": '20',
|
||||
"bonusCount": '0',
|
||||
"lastUpdateDate": "1970-01-01 09:00:00",
|
||||
"isWatched": "true"
|
||||
"presetId": "20",
|
||||
"bonusCount": "0",
|
||||
"lastUpdateDate": "1970-01-01 09:00:00",
|
||||
"isWatched": "true",
|
||||
},
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
def handle_get_user_map_api_request(self, data: Dict) -> Dict:
|
||||
user_map_list = self.data.item.get_maps(data["userId"])
|
||||
if user_map_list is None: return {}
|
||||
|
||||
if user_map_list is None:
|
||||
return {}
|
||||
|
||||
map_list = []
|
||||
for map in user_map_list:
|
||||
tmp = map._asdict()
|
||||
@@ -317,19 +333,19 @@ class ChuniBase():
|
||||
map_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userId": data["userId"],
|
||||
"length": len(map_list),
|
||||
"userMapList": map_list
|
||||
"userMapList": map_list,
|
||||
}
|
||||
|
||||
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
|
||||
music_detail = self.data.score.get_scores(data["userId"])
|
||||
if music_detail is None:
|
||||
if music_detail is None:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": 0,
|
||||
"userId": data["userId"],
|
||||
"length": 0,
|
||||
"nextIndex": -1,
|
||||
"userMusicList": [] #240
|
||||
"userMusicList": [], # 240
|
||||
}
|
||||
song_list = []
|
||||
next_idx = int(data["nextIndex"])
|
||||
@@ -340,66 +356,60 @@ class ChuniBase():
|
||||
tmp = music_detail[x]._asdict()
|
||||
tmp.pop("user")
|
||||
tmp.pop("id")
|
||||
|
||||
|
||||
for song in song_list:
|
||||
if song["userMusicDetailList"][0]["musicId"] == tmp["musicId"]:
|
||||
found = True
|
||||
song["userMusicDetailList"].append(tmp)
|
||||
song["length"] = len(song["userMusicDetailList"])
|
||||
|
||||
|
||||
if not found:
|
||||
song_list.append({
|
||||
"length": 1,
|
||||
"userMusicDetailList": [tmp]
|
||||
})
|
||||
|
||||
song_list.append({"length": 1, "userMusicDetailList": [tmp]})
|
||||
|
||||
if len(song_list) >= max_ct:
|
||||
break
|
||||
|
||||
|
||||
if len(song_list) >= max_ct:
|
||||
next_idx += max_ct
|
||||
else:
|
||||
next_idx = 0
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(song_list),
|
||||
"userId": data["userId"],
|
||||
"length": len(song_list),
|
||||
"nextIndex": next_idx,
|
||||
"userMusicList": song_list #240
|
||||
"userMusicList": song_list, # 240
|
||||
}
|
||||
|
||||
def handle_get_user_option_api_request(self, data: Dict) -> Dict:
|
||||
p = self.data.profile.get_profile_option(data["userId"])
|
||||
|
||||
|
||||
option = p._asdict()
|
||||
option.pop("id")
|
||||
option.pop("user")
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userGameOption": option
|
||||
}
|
||||
return {"userId": data["userId"], "userGameOption": option}
|
||||
|
||||
def handle_get_user_option_ex_api_request(self, data: Dict) -> Dict:
|
||||
p = self.data.profile.get_profile_option_ex(data["userId"])
|
||||
|
||||
|
||||
option = p._asdict()
|
||||
option.pop("id")
|
||||
option.pop("user")
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userGameOptionEx": option
|
||||
}
|
||||
return {"userId": data["userId"], "userGameOptionEx": option}
|
||||
|
||||
def read_wtf8(self, src):
|
||||
return bytes([ord(c) for c in src]).decode("utf-8")
|
||||
|
||||
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
profile = self.data.profile.get_profile_preview(data["userId"], self.version)
|
||||
if profile is None: return None
|
||||
profile_character = self.data.item.get_character(data["userId"], profile["characterId"])
|
||||
|
||||
if profile is None:
|
||||
return None
|
||||
profile_character = self.data.item.get_character(
|
||||
data["userId"], profile["characterId"]
|
||||
)
|
||||
|
||||
if profile_character is None:
|
||||
chara = {}
|
||||
else:
|
||||
@@ -408,8 +418,8 @@ class ChuniBase():
|
||||
chara.pop("user")
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
# Current Login State
|
||||
"userId": data["userId"],
|
||||
# Current Login State
|
||||
"isLogin": False,
|
||||
"lastLoginDate": profile["lastPlayDate"],
|
||||
# User Profile
|
||||
@@ -421,14 +431,14 @@ class ChuniBase():
|
||||
"lastGameId": profile["lastGameId"],
|
||||
"lastRomVersion": profile["lastRomVersion"],
|
||||
"lastDataVersion": profile["lastDataVersion"],
|
||||
"lastPlayDate": profile["lastPlayDate"],
|
||||
"trophyId": profile["trophyId"],
|
||||
"lastPlayDate": profile["lastPlayDate"],
|
||||
"trophyId": profile["trophyId"],
|
||||
"nameplateId": profile["nameplateId"],
|
||||
# Current Selected Character
|
||||
"userCharacter": chara,
|
||||
# User Game Options
|
||||
"playerLevel": profile["playerLevel"],
|
||||
"rating": profile["rating"],
|
||||
"playerLevel": profile["playerLevel"],
|
||||
"rating": profile["rating"],
|
||||
"headphone": profile["headphone"],
|
||||
"chargeState": "1",
|
||||
"userNameEx": profile["userName"],
|
||||
@@ -436,7 +446,7 @@ class ChuniBase():
|
||||
|
||||
def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
|
||||
recet_rating_list = self.data.profile.get_profile_recent_rating(data["userId"])
|
||||
if recet_rating_list is None:
|
||||
if recet_rating_list is None:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": 0,
|
||||
@@ -459,11 +469,8 @@ class ChuniBase():
|
||||
|
||||
def handle_get_user_team_api_request(self, data: Dict) -> Dict:
|
||||
# TODO: Team
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"teamId": 0
|
||||
}
|
||||
|
||||
return {"userId": data["userId"], "teamId": 0}
|
||||
|
||||
def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
@@ -486,19 +493,30 @@ class ChuniBase():
|
||||
|
||||
if "userData" in upsert:
|
||||
try:
|
||||
upsert["userData"][0]["userName"] = self.read_wtf8(upsert["userData"][0]["userName"])
|
||||
except: pass
|
||||
upsert["userData"][0]["userName"] = self.read_wtf8(
|
||||
upsert["userData"][0]["userName"]
|
||||
)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.data.profile.put_profile_data(user_id, self.version, upsert["userData"][0])
|
||||
self.data.profile.put_profile_data(
|
||||
user_id, self.version, upsert["userData"][0]
|
||||
)
|
||||
if "userDataEx" in upsert:
|
||||
self.data.profile.put_profile_data_ex(user_id, self.version, upsert["userDataEx"][0])
|
||||
self.data.profile.put_profile_data_ex(
|
||||
user_id, self.version, upsert["userDataEx"][0]
|
||||
)
|
||||
if "userGameOption" in upsert:
|
||||
self.data.profile.put_profile_option(user_id, upsert["userGameOption"][0])
|
||||
if "userGameOptionEx" in upsert:
|
||||
self.data.profile.put_profile_option_ex(user_id, upsert["userGameOptionEx"][0])
|
||||
self.data.profile.put_profile_option_ex(
|
||||
user_id, upsert["userGameOptionEx"][0]
|
||||
)
|
||||
if "userRecentRatingList" in upsert:
|
||||
self.data.profile.put_profile_recent_rating(user_id, upsert["userRecentRatingList"])
|
||||
|
||||
self.data.profile.put_profile_recent_rating(
|
||||
user_id, upsert["userRecentRatingList"]
|
||||
)
|
||||
|
||||
if "userCharacterList" in upsert:
|
||||
for character in upsert["userCharacterList"]:
|
||||
self.data.item.put_character(user_id, character)
|
||||
@@ -514,7 +532,7 @@ class ChuniBase():
|
||||
if "userDuelList" in upsert:
|
||||
for duel in upsert["userDuelList"]:
|
||||
self.data.item.put_duel(user_id, duel)
|
||||
|
||||
|
||||
if "userItemList" in upsert:
|
||||
for item in upsert["userItemList"]:
|
||||
self.data.item.put_item(user_id, item)
|
||||
@@ -522,23 +540,23 @@ class ChuniBase():
|
||||
if "userActivityList" in upsert:
|
||||
for activity in upsert["userActivityList"]:
|
||||
self.data.profile.put_profile_activity(user_id, activity)
|
||||
|
||||
|
||||
if "userChargeList" in upsert:
|
||||
for charge in upsert["userChargeList"]:
|
||||
self.data.profile.put_profile_charge(user_id, charge)
|
||||
|
||||
|
||||
if "userMusicDetailList" in upsert:
|
||||
for song in upsert["userMusicDetailList"]:
|
||||
self.data.score.put_score(user_id, song)
|
||||
|
||||
|
||||
if "userPlaylogList" in upsert:
|
||||
for playlog in upsert["userPlaylogList"]:
|
||||
self.data.score.put_playlog(user_id, playlog)
|
||||
|
||||
|
||||
if "userTeamPoint" in upsert:
|
||||
# TODO: team stuff
|
||||
pass
|
||||
|
||||
|
||||
if "userMapAreaList" in upsert:
|
||||
for map_area in upsert["userMapAreaList"]:
|
||||
self.data.item.put_map_area(user_id, map_area)
|
||||
@@ -551,22 +569,22 @@ class ChuniBase():
|
||||
for emoney in upsert["userEmoneyList"]:
|
||||
self.data.profile.put_profile_emoney(user_id, emoney)
|
||||
|
||||
return { "returnCode": "1" }
|
||||
return {"returnCode": "1"}
|
||||
|
||||
def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
|
||||
return { "returnCode": "1" }
|
||||
return {"returnCode": "1"}
|
||||
|
||||
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||
return { "returnCode": "1" }
|
||||
return {"returnCode": "1"}
|
||||
|
||||
def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict:
|
||||
return { "returnCode": "1" }
|
||||
return {"returnCode": "1"}
|
||||
|
||||
def handle_upsert_client_error_api_request(self, data: Dict) -> Dict:
|
||||
return { "returnCode": "1" }
|
||||
return {"returnCode": "1"}
|
||||
|
||||
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
||||
return { "returnCode": "1" }
|
||||
return {"returnCode": "1"}
|
||||
|
||||
def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
|
||||
return { "returnCode": "1" }
|
||||
return {"returnCode": "1"}
|
||||
|
||||
@@ -1,36 +1,49 @@
|
||||
from core.config import CoreConfig
|
||||
from typing import Dict
|
||||
|
||||
class ChuniServerConfig():
|
||||
|
||||
class ChuniServerConfig:
|
||||
def __init__(self, parent_config: "ChuniConfig") -> None:
|
||||
self.__config = parent_config
|
||||
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(self.__config, 'chuni', 'server', 'enable', default=True)
|
||||
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "chuni", "server", "enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def loglevel(self) -> int:
|
||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'chuni', 'server', 'loglevel', default="info"))
|
||||
return CoreConfig.str_to_loglevel(
|
||||
CoreConfig.get_config_field(
|
||||
self.__config, "chuni", "server", "loglevel", default="info"
|
||||
)
|
||||
)
|
||||
|
||||
class ChuniCryptoConfig():
|
||||
|
||||
class ChuniCryptoConfig:
|
||||
def __init__(self, parent_config: "ChuniConfig") -> None:
|
||||
self.__config = parent_config
|
||||
|
||||
|
||||
@property
|
||||
def keys(self) -> Dict:
|
||||
"""
|
||||
in the form of:
|
||||
internal_version: [key, iv]
|
||||
internal_version: [key, iv]
|
||||
all values are hex strings
|
||||
"""
|
||||
return CoreConfig.get_config_field(self.__config, 'chuni', 'crypto', 'keys', default={})
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "chuni", "crypto", "keys", default={}
|
||||
)
|
||||
|
||||
@property
|
||||
def encrypted_only(self) -> bool:
|
||||
return CoreConfig.get_config_field(self.__config, 'chuni', 'crypto', 'encrypted_only', default=False)
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "chuni", "crypto", "encrypted_only", default=False
|
||||
)
|
||||
|
||||
|
||||
class ChuniConfig(dict):
|
||||
def __init__(self) -> None:
|
||||
self.server = ChuniServerConfig(self)
|
||||
self.crypto = ChuniCryptoConfig(self)
|
||||
self.crypto = ChuniCryptoConfig(self)
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
class ChuniConstants():
|
||||
class ChuniConstants:
|
||||
GAME_CODE = "SDBT"
|
||||
GAME_CODE_NEW = "SDHD"
|
||||
|
||||
CONFIG_NAME = "chuni.yaml"
|
||||
|
||||
VER_CHUNITHM = 0
|
||||
VER_CHUNITHM_PLUS = 1
|
||||
VER_CHUNITHM_AIR = 2
|
||||
VER_CHUNITHM_AIR_PLUS = 3
|
||||
VER_CHUNITHM_STAR = 4
|
||||
VER_CHUNITHM_STAR = 4
|
||||
VER_CHUNITHM_STAR_PLUS = 5
|
||||
VER_CHUNITHM_AMAZON = 6
|
||||
VER_CHUNITHM_AMAZON_PLUS = 7
|
||||
@@ -16,8 +18,21 @@ class ChuniConstants():
|
||||
VER_CHUNITHM_NEW = 11
|
||||
VER_CHUNITHM_NEW_PLUS = 12
|
||||
|
||||
VERSION_NAMES = ["Chunithm", "Chunithm+", "Chunithm Air", "Chunithm Air+", "Chunithm Star", "Chunithm Star+", "Chunithm Amazon",
|
||||
"Chunithm Amazon+", "Chunithm Crystal", "Chunithm Crystal+", "Chunithm Paradise", "Chunithm New!!", "Chunithm New!!+"]
|
||||
VERSION_NAMES = [
|
||||
"Chunithm",
|
||||
"Chunithm+",
|
||||
"Chunithm Air",
|
||||
"Chunithm Air+",
|
||||
"Chunithm Star",
|
||||
"Chunithm Star+",
|
||||
"Chunithm Amazon",
|
||||
"Chunithm Amazon+",
|
||||
"Chunithm Crystal",
|
||||
"Chunithm Crystal+",
|
||||
"Chunithm Paradise",
|
||||
"Chunithm New!!",
|
||||
"Chunithm New!!+",
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def game_ver_to_string(cls, ver: int):
|
||||
|
||||
@@ -7,12 +7,13 @@ from titles.chuni.base import ChuniBase
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniCrystal(ChuniBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.40.00"
|
||||
return ret
|
||||
|
||||
@@ -7,12 +7,13 @@ from titles.chuni.base import ChuniBase
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniCrystalPlus(ChuniBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.45.00"
|
||||
return ret
|
||||
|
||||
@@ -2,6 +2,7 @@ from core.data import Data
|
||||
from core.config import CoreConfig
|
||||
from titles.chuni.schema import *
|
||||
|
||||
|
||||
class ChuniData(Data):
|
||||
def __init__(self, cfg: CoreConfig) -> None:
|
||||
super().__init__(cfg)
|
||||
@@ -9,4 +10,4 @@ class ChuniData(Data):
|
||||
self.item = ChuniItemData(cfg, self.session)
|
||||
self.profile = ChuniProfileData(cfg, self.session)
|
||||
self.score = ChuniScoreData(cfg, self.session)
|
||||
self.static = ChuniStaticData(cfg, self.session)
|
||||
self.static = ChuniStaticData(cfg, self.session)
|
||||
|
||||
@@ -8,6 +8,8 @@ import inflection
|
||||
import string
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import pad
|
||||
from os import path
|
||||
from typing import Tuple
|
||||
|
||||
from core import CoreConfig
|
||||
from titles.chuni.config import ChuniConfig
|
||||
@@ -26,11 +28,15 @@ from titles.chuni.paradise import ChuniParadise
|
||||
from titles.chuni.new import ChuniNew
|
||||
from titles.chuni.newplus import ChuniNewPlus
|
||||
|
||||
class ChuniServlet():
|
||||
|
||||
class ChuniServlet:
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.game_cfg = ChuniConfig()
|
||||
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/chuni.yaml")))
|
||||
if path.exists(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"):
|
||||
self.game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
self.versions = [
|
||||
ChuniBase(core_cfg, self.game_cfg),
|
||||
@@ -53,120 +59,161 @@ class ChuniServlet():
|
||||
if not hasattr(self.logger, "inited"):
|
||||
log_fmt_str = "[%(asctime)s] Chunithm | %(levelname)s | %(message)s"
|
||||
log_fmt = logging.Formatter(log_fmt_str)
|
||||
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "chuni"), encoding='utf8',
|
||||
when="d", backupCount=10)
|
||||
fileHandler = TimedRotatingFileHandler(
|
||||
"{0}/{1}.log".format(self.core_cfg.server.log_dir, "chuni"),
|
||||
encoding="utf8",
|
||||
when="d",
|
||||
backupCount=10,
|
||||
)
|
||||
|
||||
fileHandler.setFormatter(log_fmt)
|
||||
|
||||
|
||||
consoleHandler = logging.StreamHandler()
|
||||
consoleHandler.setFormatter(log_fmt)
|
||||
|
||||
self.logger.addHandler(fileHandler)
|
||||
self.logger.addHandler(consoleHandler)
|
||||
|
||||
|
||||
self.logger.setLevel(self.game_cfg.server.loglevel)
|
||||
coloredlogs.install(level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str)
|
||||
coloredlogs.install(
|
||||
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||
)
|
||||
self.logger.inited = True
|
||||
|
||||
@classmethod
|
||||
def get_allnet_info(
|
||||
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
|
||||
) -> Tuple[bool, str, str]:
|
||||
game_cfg = ChuniConfig()
|
||||
if path.exists(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"):
|
||||
game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
if not game_cfg.server.enable:
|
||||
return (False, "", "")
|
||||
|
||||
if core_cfg.server.is_develop:
|
||||
return (
|
||||
True,
|
||||
f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/",
|
||||
"",
|
||||
)
|
||||
|
||||
return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v/", "")
|
||||
|
||||
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
|
||||
if url_path.lower() == "/ping":
|
||||
return zlib.compress(b'{"returnCode": "1"}')
|
||||
|
||||
req_raw = request.content.getvalue()
|
||||
url_split = url_path.split("/")
|
||||
encrtped = False
|
||||
internal_ver = 0
|
||||
endpoint = url_split[len(url_split) - 1]
|
||||
|
||||
if version < 105: # 1.0
|
||||
if version < 105: # 1.0
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM
|
||||
elif version >= 105 and version < 110: # Plus
|
||||
elif version >= 105 and version < 110: # Plus
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_PLUS
|
||||
elif version >= 110 and version < 115: # Air
|
||||
elif version >= 110 and version < 115: # Air
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_AIR
|
||||
elif version >= 115 and version < 120: # Air Plus
|
||||
elif version >= 115 and version < 120: # Air Plus
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_AIR_PLUS
|
||||
elif version >= 120 and version < 125: # Star
|
||||
elif version >= 120 and version < 125: # Star
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_STAR
|
||||
elif version >= 125 and version < 130: # Star Plus
|
||||
elif version >= 125 and version < 130: # Star Plus
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_STAR_PLUS
|
||||
elif version >= 130 and version < 135: # Amazon
|
||||
elif version >= 130 and version < 135: # Amazon
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_AMAZON
|
||||
elif version >= 135 and version < 140: # Amazon Plus
|
||||
elif version >= 135 and version < 140: # Amazon Plus
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_AMAZON_PLUS
|
||||
elif version >= 140 and version < 145: # Crystal
|
||||
elif version >= 140 and version < 145: # Crystal
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL
|
||||
elif version >= 145 and version < 150: # Crystal Plus
|
||||
elif version >= 145 and version < 150: # Crystal Plus
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS
|
||||
elif version >= 150 and version < 200: # Paradise
|
||||
elif version >= 150 and version < 200: # Paradise
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE
|
||||
elif version >= 200 and version < 205: # New
|
||||
elif version >= 200 and version < 205: # New
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
||||
elif version >= 205 and version < 210: # New Plus
|
||||
elif version >= 205 and version < 210: # New Plus
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
||||
|
||||
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
|
||||
# doing encrypted. The likelyhood of false positives is low but
|
||||
# If we get a 32 character long hex string, it's a hash and we're
|
||||
# doing encrypted. The likelyhood of false positives is low but
|
||||
# technically not 0
|
||||
endpoint = request.getHeader("User-Agent").split("#")[0]
|
||||
try:
|
||||
crypt = AES.new(
|
||||
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][0]),
|
||||
AES.MODE_CBC,
|
||||
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][1])
|
||||
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][0]),
|
||||
AES.MODE_CBC,
|
||||
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][1]),
|
||||
)
|
||||
|
||||
req_raw = crypt.decrypt(req_raw)
|
||||
|
||||
except:
|
||||
self.logger.error(f"Failed to decrypt v{version} request to {endpoint} -> {req_raw}")
|
||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||
self.logger.error(
|
||||
f"Failed to decrypt v{version} request to {endpoint} -> {req_raw}"
|
||||
)
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
|
||||
encrtped = True
|
||||
|
||||
if not encrtped and self.game_cfg.crypto.encrypted_only:
|
||||
self.logger.error(f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}")
|
||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||
|
||||
try:
|
||||
if not encrtped and self.game_cfg.crypto.encrypted_only:
|
||||
self.logger.error(
|
||||
f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}"
|
||||
)
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
|
||||
try:
|
||||
unzip = zlib.decompress(req_raw)
|
||||
|
||||
|
||||
except zlib.error as e:
|
||||
self.logger.error(f"Failed to decompress v{version} {endpoint} request -> {e}")
|
||||
self.logger.error(
|
||||
f"Failed to decompress v{version} {endpoint} request -> {e}"
|
||||
)
|
||||
return b""
|
||||
|
||||
|
||||
req_data = json.loads(unzip)
|
||||
|
||||
self.logger.info(f"v{version} {endpoint} request - {req_data}")
|
||||
|
||||
self.logger.info(
|
||||
f"v{version} {endpoint} request from {request.getClientAddress().host}"
|
||||
)
|
||||
self.logger.debug(req_data)
|
||||
|
||||
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
||||
|
||||
try:
|
||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||
resp = handler(req_data)
|
||||
|
||||
except AttributeError as e:
|
||||
self.logger.warning(f"Unhandled v{version} request {endpoint} - {e}")
|
||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||
resp = {"returnCode": 1}
|
||||
|
||||
else:
|
||||
try:
|
||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||
resp = handler(req_data)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||
|
||||
if resp == None:
|
||||
resp = {'returnCode': 1}
|
||||
|
||||
self.logger.info(f"Response {resp}")
|
||||
|
||||
resp = {"returnCode": 1}
|
||||
|
||||
self.logger.debug(f"Response {resp}")
|
||||
|
||||
zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||
|
||||
|
||||
if not encrtped:
|
||||
return zipped
|
||||
|
||||
padded = pad(zipped, 16)
|
||||
|
||||
crypt = AES.new(
|
||||
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][0]),
|
||||
AES.MODE_CBC,
|
||||
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][1])
|
||||
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][0]),
|
||||
AES.MODE_CBC,
|
||||
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][1]),
|
||||
)
|
||||
|
||||
return crypt.encrypt(padded)
|
||||
|
||||
@@ -9,13 +9,9 @@ from titles.chuni.database import ChuniData
|
||||
from titles.chuni.base import ChuniBase
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
class ChuniNew(ChuniBase):
|
||||
|
||||
ITEM_TYPE = {
|
||||
"character": 20,
|
||||
"story": 21,
|
||||
"card": 22
|
||||
}
|
||||
class ChuniNew(ChuniBase):
|
||||
ITEM_TYPE = {"character": 20, "story": 21, "card": 22}
|
||||
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
@@ -25,12 +21,20 @@ class ChuniNew(ChuniBase):
|
||||
self.logger = logging.getLogger("chuni")
|
||||
self.game = ChuniConstants.GAME_CODE
|
||||
self.version = ChuniConstants.VER_CHUNITHM_NEW
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
match_start = datetime.strftime(datetime.now() - timedelta(hours=10), self.date_time_format)
|
||||
match_end = datetime.strftime(datetime.now() + timedelta(hours=10), self.date_time_format)
|
||||
reboot_start = datetime.strftime(datetime.now() - timedelta(hours=11), self.date_time_format)
|
||||
reboot_end = datetime.strftime(datetime.now() - timedelta(hours=10), self.date_time_format)
|
||||
match_start = datetime.strftime(
|
||||
datetime.now() - timedelta(hours=10), self.date_time_format
|
||||
)
|
||||
match_end = datetime.strftime(
|
||||
datetime.now() + timedelta(hours=10), self.date_time_format
|
||||
)
|
||||
reboot_start = datetime.strftime(
|
||||
datetime.now() - timedelta(hours=11), self.date_time_format
|
||||
)
|
||||
reboot_end = datetime.strftime(
|
||||
datetime.now() - timedelta(hours=10), self.date_time_format
|
||||
)
|
||||
return {
|
||||
"gameSetting": {
|
||||
"isMaintenance": "false",
|
||||
@@ -47,21 +51,21 @@ class ChuniNew(ChuniBase):
|
||||
"matchErrorLimit": 9999,
|
||||
"romVersion": "2.00.00",
|
||||
"dataVersion": "2.00.00",
|
||||
"matchingUri": f"http://{self.core_cfg.server.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
"matchingUriX": f"http://{self.core_cfg.server.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
"udpHolePunchUri": f"http://{self.core_cfg.server.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
"reflectorUri": f"http://{self.core_cfg.server.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
"matchingUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
"matchingUriX": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
"udpHolePunchUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
"reflectorUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
},
|
||||
"isDumpUpload": "false",
|
||||
"isAou": "false",
|
||||
"isDumpUpload": "false",
|
||||
"isAou": "false",
|
||||
}
|
||||
|
||||
|
||||
def handle_delete_token_api_request(self, data: Dict) -> Dict:
|
||||
return { "returnCode": "1" }
|
||||
|
||||
return {"returnCode": "1"}
|
||||
|
||||
def handle_create_token_api_request(self, data: Dict) -> Dict:
|
||||
return { "returnCode": "1" }
|
||||
|
||||
return {"returnCode": "1"}
|
||||
|
||||
def handle_get_user_map_area_api_request(self, data: Dict) -> Dict:
|
||||
user_map_areas = self.data.item.get_map_areas(data["userId"])
|
||||
|
||||
@@ -72,32 +76,29 @@ class ChuniNew(ChuniBase):
|
||||
tmp.pop("user")
|
||||
map_areas.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userMapAreaList": map_areas
|
||||
}
|
||||
|
||||
return {"userId": data["userId"], "userMapAreaList": map_areas}
|
||||
|
||||
def handle_get_user_symbol_chat_setting_api_request(self, data: Dict) -> Dict:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"symbolCharInfoList": []
|
||||
}
|
||||
return {"userId": data["userId"], "symbolCharInfoList": []}
|
||||
|
||||
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
profile = self.data.profile.get_profile_preview(data["userId"], self.version)
|
||||
if profile is None: return None
|
||||
profile_character = self.data.item.get_character(data["userId"], profile["characterId"])
|
||||
|
||||
if profile is None:
|
||||
return None
|
||||
profile_character = self.data.item.get_character(
|
||||
data["userId"], profile["characterId"]
|
||||
)
|
||||
|
||||
if profile_character is None:
|
||||
chara = {}
|
||||
else:
|
||||
chara = profile_character._asdict()
|
||||
chara.pop("id")
|
||||
chara.pop("user")
|
||||
|
||||
|
||||
data1 = {
|
||||
"userId": data["userId"],
|
||||
# Current Login State
|
||||
"userId": data["userId"],
|
||||
# Current Login State
|
||||
"isLogin": False,
|
||||
"lastLoginDate": profile["lastPlayDate"],
|
||||
# User Profile
|
||||
@@ -109,14 +110,14 @@ class ChuniNew(ChuniBase):
|
||||
"lastGameId": profile["lastGameId"],
|
||||
"lastRomVersion": profile["lastRomVersion"],
|
||||
"lastDataVersion": profile["lastDataVersion"],
|
||||
"lastPlayDate": profile["lastPlayDate"],
|
||||
"lastPlayDate": profile["lastPlayDate"],
|
||||
"emoneyBrandId": 0,
|
||||
"trophyId": profile["trophyId"],
|
||||
"trophyId": profile["trophyId"],
|
||||
# Current Selected Character
|
||||
"userCharacter": chara,
|
||||
# User Game Options
|
||||
"playerLevel": profile["playerLevel"],
|
||||
"rating": profile["rating"],
|
||||
"playerLevel": profile["playerLevel"],
|
||||
"rating": profile["rating"],
|
||||
"headphone": profile["headphone"],
|
||||
"chargeState": 0,
|
||||
"userNameEx": "0",
|
||||
|
||||
@@ -7,17 +7,26 @@ from titles.chuni.new import ChuniNew
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniNewPlus(ChuniNew):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["romVersion"] = "2.05.00"
|
||||
ret["gameSetting"]["dataVersion"] = "2.05.00"
|
||||
ret["gameSetting"]["matchingUri"] = f"http://{self.core_cfg.server.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
ret["gameSetting"]["matchingUriX"] = f"http://{self.core_cfg.server.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
ret["gameSetting"]["udpHolePunchUri"] = f"http://{self.core_cfg.server.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
ret["gameSetting"]["reflectorUri"] = f"http://{self.core_cfg.server.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"matchingUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"matchingUriX"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"udpHolePunchUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"reflectorUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
return ret
|
||||
|
||||
@@ -7,12 +7,13 @@ from titles.chuni.base import ChuniBase
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniParadise(ChuniBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_PARADISE
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.50.00"
|
||||
return ret
|
||||
|
||||
@@ -5,12 +5,13 @@ from titles.chuni.base import ChuniBase
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniPlus(ChuniBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_PLUS
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.05.00"
|
||||
return ret
|
||||
return ret
|
||||
|
||||
@@ -7,48 +7,60 @@ from core.config import CoreConfig
|
||||
from titles.chuni.database import ChuniData
|
||||
from titles.chuni.const import ChuniConstants
|
||||
|
||||
|
||||
class ChuniReader(BaseReader):
|
||||
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str], opt_dir: Optional[str], extra: Optional[str]) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
config: CoreConfig,
|
||||
version: int,
|
||||
bin_dir: Optional[str],
|
||||
opt_dir: Optional[str],
|
||||
extra: Optional[str],
|
||||
) -> None:
|
||||
super().__init__(config, version, bin_dir, opt_dir, extra)
|
||||
self.data = ChuniData(config)
|
||||
|
||||
try:
|
||||
self.logger.info(f"Start importer for {ChuniConstants.game_ver_to_string(version)}")
|
||||
self.logger.info(
|
||||
f"Start importer for {ChuniConstants.game_ver_to_string(version)}"
|
||||
)
|
||||
except IndexError:
|
||||
self.logger.error(f"Invalid chunithm version {version}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def read(self) -> None:
|
||||
data_dirs = []
|
||||
if self.bin_dir is not None:
|
||||
data_dirs += self.get_data_directories(self.bin_dir)
|
||||
|
||||
|
||||
if self.opt_dir is not None:
|
||||
data_dirs += self.get_data_directories(self.opt_dir)
|
||||
|
||||
data_dirs += self.get_data_directories(self.opt_dir)
|
||||
|
||||
for dir in data_dirs:
|
||||
self.logger.info(f"Read from {dir}")
|
||||
self.read_events(f"{dir}/event")
|
||||
self.read_music(f"{dir}/music")
|
||||
self.read_charges(f"{dir}/chargeItem")
|
||||
self.read_avatar(f"{dir}/avatarAccessory")
|
||||
|
||||
|
||||
def read_events(self, evt_dir: str) -> None:
|
||||
for root, dirs, files in walk(evt_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/Event.xml"):
|
||||
with open(f"{root}/{dir}/Event.xml", 'rb') as fp:
|
||||
with open(f"{root}/{dir}/Event.xml", "rb") as fp:
|
||||
bytedata = fp.read()
|
||||
strdata = bytedata.decode('UTF-8')
|
||||
strdata = bytedata.decode("UTF-8")
|
||||
|
||||
xml_root = ET.fromstring(strdata)
|
||||
for name in xml_root.findall('name'):
|
||||
id = name.find('id').text
|
||||
name = name.find('str').text
|
||||
for substances in xml_root.findall('substances'):
|
||||
event_type = substances.find('type').text
|
||||
|
||||
result = self.data.static.put_event(self.version, id, event_type, name)
|
||||
for name in xml_root.findall("name"):
|
||||
id = name.find("id").text
|
||||
name = name.find("str").text
|
||||
for substances in xml_root.findall("substances"):
|
||||
event_type = substances.find("type").text
|
||||
|
||||
result = self.data.static.put_event(
|
||||
self.version, id, event_type, name
|
||||
)
|
||||
if result is not None:
|
||||
self.logger.info(f"Inserted event {id}")
|
||||
else:
|
||||
@@ -58,73 +70,90 @@ class ChuniReader(BaseReader):
|
||||
for root, dirs, files in walk(music_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/Music.xml"):
|
||||
with open(f"{root}/{dir}/Music.xml", 'rb') as fp:
|
||||
with open(f"{root}/{dir}/Music.xml", "rb") as fp:
|
||||
bytedata = fp.read()
|
||||
strdata = bytedata.decode('UTF-8')
|
||||
strdata = bytedata.decode("UTF-8")
|
||||
|
||||
xml_root = ET.fromstring(strdata)
|
||||
for name in xml_root.findall('name'):
|
||||
song_id = name.find('id').text
|
||||
title = name.find('str').text
|
||||
for name in xml_root.findall("name"):
|
||||
song_id = name.find("id").text
|
||||
title = name.find("str").text
|
||||
|
||||
for artistName in xml_root.findall('artistName'):
|
||||
artist = artistName.find('str').text
|
||||
for artistName in xml_root.findall("artistName"):
|
||||
artist = artistName.find("str").text
|
||||
|
||||
for genreNames in xml_root.findall('genreNames'):
|
||||
for list_ in genreNames.findall('list'):
|
||||
for StringID in list_.findall('StringID'):
|
||||
genre = StringID.find('str').text
|
||||
for genreNames in xml_root.findall("genreNames"):
|
||||
for list_ in genreNames.findall("list"):
|
||||
for StringID in list_.findall("StringID"):
|
||||
genre = StringID.find("str").text
|
||||
|
||||
for jaketFile in xml_root.findall('jaketFile'): #nice typo, SEGA
|
||||
jacket_path = jaketFile.find('path').text
|
||||
for jaketFile in xml_root.findall("jaketFile"): # nice typo, SEGA
|
||||
jacket_path = jaketFile.find("path").text
|
||||
|
||||
for fumens in xml_root.findall('fumens'):
|
||||
for MusicFumenData in fumens.findall('MusicFumenData'):
|
||||
fumen_path = MusicFumenData.find('file').find("path")
|
||||
|
||||
if fumen_path.text is not None:
|
||||
chart_id = MusicFumenData.find('type').find('id').text
|
||||
for fumens in xml_root.findall("fumens"):
|
||||
for MusicFumenData in fumens.findall("MusicFumenData"):
|
||||
fumen_path = MusicFumenData.find("file").find("path")
|
||||
|
||||
if fumen_path is not None:
|
||||
chart_id = MusicFumenData.find("type").find("id").text
|
||||
if chart_id == "4":
|
||||
level = float(xml_root.find("starDifType").text)
|
||||
we_chara = xml_root.find("worldsEndTagName").find("str").text
|
||||
we_chara = (
|
||||
xml_root.find("worldsEndTagName")
|
||||
.find("str")
|
||||
.text
|
||||
)
|
||||
else:
|
||||
level = float(f"{MusicFumenData.find('level').text}.{MusicFumenData.find('levelDecimal').text}")
|
||||
level = float(
|
||||
f"{MusicFumenData.find('level').text}.{MusicFumenData.find('levelDecimal').text}"
|
||||
)
|
||||
we_chara = None
|
||||
|
||||
|
||||
result = self.data.static.put_music(
|
||||
self.version,
|
||||
song_id,
|
||||
chart_id,
|
||||
chart_id,
|
||||
title,
|
||||
artist,
|
||||
level,
|
||||
genre,
|
||||
jacket_path,
|
||||
we_chara
|
||||
we_chara,
|
||||
)
|
||||
|
||||
if result is not None:
|
||||
self.logger.info(f"Inserted music {song_id} chart {chart_id}")
|
||||
self.logger.info(
|
||||
f"Inserted music {song_id} chart {chart_id}"
|
||||
)
|
||||
else:
|
||||
self.logger.warn(f"Failed to insert music {song_id} chart {chart_id}")
|
||||
self.logger.warn(
|
||||
f"Failed to insert music {song_id} chart {chart_id}"
|
||||
)
|
||||
|
||||
def read_charges(self, charge_dir: str) -> None:
|
||||
for root, dirs, files in walk(charge_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/ChargeItem.xml"):
|
||||
with open(f"{root}/{dir}/ChargeItem.xml", 'rb') as fp:
|
||||
with open(f"{root}/{dir}/ChargeItem.xml", "rb") as fp:
|
||||
bytedata = fp.read()
|
||||
strdata = bytedata.decode('UTF-8')
|
||||
strdata = bytedata.decode("UTF-8")
|
||||
|
||||
xml_root = ET.fromstring(strdata)
|
||||
for name in xml_root.findall('name'):
|
||||
id = name.find('id').text
|
||||
name = name.find('str').text
|
||||
expirationDays = xml_root.find('expirationDays').text
|
||||
consumeType = xml_root.find('consumeType').text
|
||||
sellingAppeal = bool(xml_root.find('sellingAppeal').text)
|
||||
for name in xml_root.findall("name"):
|
||||
id = name.find("id").text
|
||||
name = name.find("str").text
|
||||
expirationDays = xml_root.find("expirationDays").text
|
||||
consumeType = xml_root.find("consumeType").text
|
||||
sellingAppeal = bool(xml_root.find("sellingAppeal").text)
|
||||
|
||||
result = self.data.static.put_charge(self.version, id, name, expirationDays, consumeType, sellingAppeal)
|
||||
result = self.data.static.put_charge(
|
||||
self.version,
|
||||
id,
|
||||
name,
|
||||
expirationDays,
|
||||
consumeType,
|
||||
sellingAppeal,
|
||||
)
|
||||
|
||||
if result is not None:
|
||||
self.logger.info(f"Inserted charge {id}")
|
||||
@@ -135,21 +164,23 @@ class ChuniReader(BaseReader):
|
||||
for root, dirs, files in walk(avatar_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/AvatarAccessory.xml"):
|
||||
with open(f"{root}/{dir}/AvatarAccessory.xml", 'rb') as fp:
|
||||
with open(f"{root}/{dir}/AvatarAccessory.xml", "rb") as fp:
|
||||
bytedata = fp.read()
|
||||
strdata = bytedata.decode('UTF-8')
|
||||
strdata = bytedata.decode("UTF-8")
|
||||
|
||||
xml_root = ET.fromstring(strdata)
|
||||
for name in xml_root.findall('name'):
|
||||
id = name.find('id').text
|
||||
name = name.find('str').text
|
||||
category = xml_root.find('category').text
|
||||
for image in xml_root.findall('image'):
|
||||
iconPath = image.find('path').text
|
||||
for texture in xml_root.findall('texture'):
|
||||
texturePath = texture.find('path').text
|
||||
for name in xml_root.findall("name"):
|
||||
id = name.find("id").text
|
||||
name = name.find("str").text
|
||||
category = xml_root.find("category").text
|
||||
for image in xml_root.findall("image"):
|
||||
iconPath = image.find("path").text
|
||||
for texture in xml_root.findall("texture"):
|
||||
texturePath = texture.find("path").text
|
||||
|
||||
result = self.data.static.put_avatar(self.version, id, name, category, iconPath, texturePath)
|
||||
result = self.data.static.put_avatar(
|
||||
self.version, id, name, category, iconPath, texturePath
|
||||
)
|
||||
|
||||
if result is not None:
|
||||
self.logger.info(f"Inserted avatarAccessory {id}")
|
||||
|
||||
@@ -3,4 +3,4 @@ from titles.chuni.schema.score import ChuniScoreData
|
||||
from titles.chuni.schema.item import ChuniItemData
|
||||
from titles.chuni.schema.static import ChuniStaticData
|
||||
|
||||
__all__ = ["ChuniProfileData", "ChuniScoreData", "ChuniItemData", "ChuniStaticData"]
|
||||
__all__ = ["ChuniProfileData", "ChuniScoreData", "ChuniItemData", "ChuniStaticData"]
|
||||
|
||||
@@ -13,7 +13,11 @@ character = Table(
|
||||
"chuni_item_character",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("characterId", Integer),
|
||||
Column("level", Integer),
|
||||
Column("param1", Integer),
|
||||
@@ -26,27 +30,35 @@ character = Table(
|
||||
Column("assignIllust", Integer),
|
||||
Column("exMaxLv", Integer),
|
||||
UniqueConstraint("user", "characterId", name="chuni_item_character_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
item = Table(
|
||||
"chuni_item_item",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("itemId", Integer),
|
||||
Column("itemKind", Integer),
|
||||
Column("stock", Integer),
|
||||
Column("isValid", Boolean),
|
||||
UniqueConstraint("user", "itemId", "itemKind", name="chuni_item_item_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
duel = Table(
|
||||
"chuni_item_duel",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("duelId", Integer),
|
||||
Column("progress", Integer),
|
||||
Column("point", Integer),
|
||||
@@ -57,14 +69,18 @@ duel = Table(
|
||||
Column("param3", Integer),
|
||||
Column("param4", Integer),
|
||||
UniqueConstraint("user", "duelId", name="chuni_item_duel_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
map = Table(
|
||||
"chuni_item_map",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("mapId", Integer),
|
||||
Column("position", Integer),
|
||||
Column("isClear", Boolean),
|
||||
@@ -72,17 +88,21 @@ map = Table(
|
||||
Column("routeNumber", Integer),
|
||||
Column("eventId", Integer),
|
||||
Column("rate", Integer),
|
||||
Column("statusCount", Integer),
|
||||
Column("statusCount", Integer),
|
||||
Column("isValid", Boolean),
|
||||
UniqueConstraint("user", "mapId", name="chuni_item_map_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
map_area = Table(
|
||||
"chuni_item_map_area",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("mapAreaId", Integer),
|
||||
Column("rate", Integer),
|
||||
Column("isClear", Boolean),
|
||||
@@ -91,9 +111,10 @@ map_area = Table(
|
||||
Column("statusCount", Integer),
|
||||
Column("remainGridCount", Integer),
|
||||
UniqueConstraint("user", "mapAreaId", name="chuni_item_map_area_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class ChuniItemData(BaseData):
|
||||
def put_character(self, user_id: int, character_data: Dict) -> Optional[int]:
|
||||
character_data["user"] = user_id
|
||||
@@ -104,24 +125,26 @@ class ChuniItemData(BaseData):
|
||||
conflict = sql.on_duplicate_key_update(**character_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_character(self, user_id: int, character_id: int) -> Optional[Dict]:
|
||||
sql = select(character).where(and_(
|
||||
character.c.user == user_id,
|
||||
character.c.characterId == character_id
|
||||
))
|
||||
|
||||
sql = select(character).where(
|
||||
and_(character.c.user == user_id, character.c.characterId == character_id)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
|
||||
def get_characters(self, user_id: int) -> Optional[List[Row]]:
|
||||
sql = select(character).where(character.c.user == user_id)
|
||||
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_item(self, user_id: int, item_data: Dict) -> Optional[int]:
|
||||
@@ -133,22 +156,23 @@ class ChuniItemData(BaseData):
|
||||
conflict = sql.on_duplicate_key_update(**item_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_items(self, user_id: int, kind: int = None) -> Optional[List[Row]]:
|
||||
if kind is None:
|
||||
sql = select(item).where(item.c.user == user_id)
|
||||
else:
|
||||
sql = select(item).where(and_(
|
||||
item.c.user == user_id,
|
||||
item.c.itemKind == kind
|
||||
))
|
||||
|
||||
sql = select(item).where(
|
||||
and_(item.c.user == user_id, item.c.itemKind == kind)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def put_duel(self, user_id: int, duel_data: Dict) -> Optional[int]:
|
||||
duel_data["user"] = user_id
|
||||
|
||||
@@ -158,14 +182,16 @@ class ChuniItemData(BaseData):
|
||||
conflict = sql.on_duplicate_key_update(**duel_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_duels(self, user_id: int) -> Optional[List[Row]]:
|
||||
sql = select(duel).where(duel.c.user == user_id)
|
||||
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_map(self, user_id: int, map_data: Dict) -> Optional[int]:
|
||||
@@ -177,16 +203,18 @@ class ChuniItemData(BaseData):
|
||||
conflict = sql.on_duplicate_key_update(**map_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_maps(self, user_id: int) -> Optional[List[Row]]:
|
||||
sql = select(map).where(map.c.user == user_id)
|
||||
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def put_map_area(self, user_id: int, map_area_data: Dict) -> Optional[int]:
|
||||
map_area_data["user"] = user_id
|
||||
|
||||
@@ -196,12 +224,14 @@ class ChuniItemData(BaseData):
|
||||
conflict = sql.on_duplicate_key_update(**map_area_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_map_areas(self, user_id: int) -> Optional[List[Row]]:
|
||||
sql = select(map_area).where(map_area.c.user == user_id)
|
||||
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
return result.fetchall()
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
@@ -13,7 +13,11 @@ profile = Table(
|
||||
"chuni_profile_data",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("exp", Integer),
|
||||
Column("level", Integer),
|
||||
@@ -62,7 +66,7 @@ profile = Table(
|
||||
Column("firstTutorialCancelNum", Integer),
|
||||
Column("totalAdvancedHighScore", Integer),
|
||||
Column("masterTutorialCancelNum", Integer),
|
||||
Column("ext1", Integer), # Added in chunew
|
||||
Column("ext1", Integer), # Added in chunew
|
||||
Column("ext2", Integer),
|
||||
Column("ext3", Integer),
|
||||
Column("ext4", Integer),
|
||||
@@ -71,16 +75,20 @@ profile = Table(
|
||||
Column("ext7", Integer),
|
||||
Column("ext8", Integer),
|
||||
Column("ext9", Integer),
|
||||
Column("ext10", Integer),
|
||||
Column("ext10", Integer),
|
||||
Column("extStr1", String(255)),
|
||||
Column("extStr2", String(255)),
|
||||
Column("extLong1", Integer),
|
||||
Column("extLong2", Integer),
|
||||
Column("mapIconId", Integer),
|
||||
Column("compatibleCmVersion", String(25)),
|
||||
Column("medal", Integer),
|
||||
Column("medal", Integer),
|
||||
Column("voiceId", Integer),
|
||||
Column("teamId", Integer, ForeignKey("chuni_profile_team.id", ondelete="SET NULL", onupdate="SET NULL")),
|
||||
Column(
|
||||
"teamId",
|
||||
Integer,
|
||||
ForeignKey("chuni_profile_team.id", ondelete="SET NULL", onupdate="SET NULL"),
|
||||
),
|
||||
Column("avatarBack", Integer, server_default="0"),
|
||||
Column("avatarFace", Integer, server_default="0"),
|
||||
Column("eliteRankPoint", Integer, server_default="0"),
|
||||
@@ -121,14 +129,18 @@ profile = Table(
|
||||
Column("netBattleEndState", Integer, server_default="0"),
|
||||
Column("avatarHead", Integer, server_default="0"),
|
||||
UniqueConstraint("user", "version", name="chuni_profile_profile_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
profile_ex = Table(
|
||||
"chuni_profile_data_ex",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("ext1", Integer),
|
||||
Column("ext2", Integer),
|
||||
@@ -165,14 +177,18 @@ profile_ex = Table(
|
||||
Column("mapIconId", Integer),
|
||||
Column("compatibleCmVersion", String(25)),
|
||||
UniqueConstraint("user", "version", name="chuni_profile_data_ex_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
option = Table(
|
||||
"chuni_profile_option",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("speed", Integer),
|
||||
Column("bgInfo", Integer),
|
||||
Column("rating", Integer),
|
||||
@@ -195,7 +211,7 @@ option = Table(
|
||||
Column("successSkill", Integer),
|
||||
Column("successSlideHold", Integer),
|
||||
Column("successTapTimbre", Integer),
|
||||
Column("ext1", Integer), # Added in chunew
|
||||
Column("ext1", Integer), # Added in chunew
|
||||
Column("ext2", Integer),
|
||||
Column("ext3", Integer),
|
||||
Column("ext4", Integer),
|
||||
@@ -224,14 +240,18 @@ option = Table(
|
||||
Column("playTimingOffset", Integer, server_default="0"),
|
||||
Column("fieldWallPosition_120", Integer, server_default="0"),
|
||||
UniqueConstraint("user", name="chuni_profile_option_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
option_ex = Table(
|
||||
"chuni_profile_option_ex",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("ext1", Integer),
|
||||
Column("ext2", Integer),
|
||||
Column("ext3", Integer),
|
||||
@@ -253,51 +273,69 @@ option_ex = Table(
|
||||
Column("ext19", Integer),
|
||||
Column("ext20", Integer),
|
||||
UniqueConstraint("user", name="chuni_profile_option_ex_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
recent_rating = Table(
|
||||
"chuni_profile_recent_rating",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("recentRating", JSON),
|
||||
UniqueConstraint("user", name="chuni_profile_recent_rating_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
region = Table(
|
||||
"chuni_profile_region",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("regionId", Integer),
|
||||
Column("playCount", Integer),
|
||||
UniqueConstraint("user", "regionId", name="chuni_profile_region_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
activity = Table(
|
||||
"chuni_profile_activity",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("kind", Integer),
|
||||
Column("activityId", Integer), # Reminder: Change this to ID in base.py or the game will be sad
|
||||
Column(
|
||||
"activityId", Integer
|
||||
), # Reminder: Change this to ID in base.py or the game will be sad
|
||||
Column("sortNumber", Integer),
|
||||
Column("param1", Integer),
|
||||
Column("param2", Integer),
|
||||
Column("param3", Integer),
|
||||
Column("param4", Integer),
|
||||
UniqueConstraint("user", "kind", "activityId", name="chuni_profile_activity_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
charge = Table(
|
||||
"chuni_profile_charge",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("chargeId", Integer),
|
||||
Column("stock", Integer),
|
||||
Column("purchaseDate", String(25)),
|
||||
@@ -306,14 +344,18 @@ charge = Table(
|
||||
Column("param2", Integer),
|
||||
Column("paramDate", String(25)),
|
||||
UniqueConstraint("user", "chargeId", name="chuni_profile_charge_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
emoney = Table(
|
||||
"chuni_profile_emoney",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("ext1", Integer),
|
||||
Column("ext2", Integer),
|
||||
Column("ext3", Integer),
|
||||
@@ -321,20 +363,24 @@ emoney = Table(
|
||||
Column("emoneyBrand", Integer),
|
||||
Column("emoneyCredit", Integer),
|
||||
UniqueConstraint("user", "emoneyBrand", name="chuni_profile_emoney_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
overpower = Table(
|
||||
"chuni_profile_overpower",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("genreId", Integer),
|
||||
Column("difficulty", Integer),
|
||||
Column("rate", Integer),
|
||||
Column("point", Integer),
|
||||
UniqueConstraint("user", "genreId", "difficulty", name="chuni_profile_emoney_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
team = Table(
|
||||
@@ -343,18 +389,21 @@ team = Table(
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("teamName", String(255)),
|
||||
Column("teamPoint", Integer),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class ChuniProfileData(BaseData):
|
||||
def put_profile_data(self, aime_id: int, version: int, profile_data: Dict) -> Optional[int]:
|
||||
def put_profile_data(
|
||||
self, aime_id: int, version: int, profile_data: Dict
|
||||
) -> Optional[int]:
|
||||
profile_data["user"] = aime_id
|
||||
profile_data["version"] = version
|
||||
if "accessCode" in profile_data:
|
||||
profile_data.pop("accessCode")
|
||||
|
||||
|
||||
profile_data = self.fix_bools(profile_data)
|
||||
|
||||
|
||||
sql = insert(profile).values(**profile_data)
|
||||
conflict = sql.on_duplicate_key_update(**profile_data)
|
||||
result = self.execute(conflict)
|
||||
@@ -363,51 +412,64 @@ class ChuniProfileData(BaseData):
|
||||
self.logger.warn(f"put_profile_data: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_profile_preview(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select([profile, option]).join(option, profile.c.user == option.c.user).filter(
|
||||
and_(profile.c.user == aime_id, profile.c.version == version)
|
||||
sql = (
|
||||
select([profile, option])
|
||||
.join(option, profile.c.user == option.c.user)
|
||||
.filter(and_(profile.c.user == aime_id, profile.c.version == version))
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_profile_data(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(profile).where(and_(
|
||||
profile.c.user == aime_id,
|
||||
profile.c.version == version,
|
||||
))
|
||||
sql = select(profile).where(
|
||||
and_(
|
||||
profile.c.user == aime_id,
|
||||
profile.c.version == version,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_profile_data_ex(self, aime_id: int, version: int, profile_ex_data: Dict) -> Optional[int]:
|
||||
|
||||
def put_profile_data_ex(
|
||||
self, aime_id: int, version: int, profile_ex_data: Dict
|
||||
) -> Optional[int]:
|
||||
profile_ex_data["user"] = aime_id
|
||||
profile_ex_data["version"] = version
|
||||
if "accessCode" in profile_ex_data:
|
||||
profile_ex_data.pop("accessCode")
|
||||
|
||||
|
||||
sql = insert(profile_ex).values(**profile_ex_data)
|
||||
conflict = sql.on_duplicate_key_update(**profile_ex_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_data_ex: Failed to update! aime_id: {aime_id}")
|
||||
self.logger.warn(
|
||||
f"put_profile_data_ex: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_profile_data_ex(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(profile_ex).where(and_(
|
||||
profile_ex.c.user == aime_id,
|
||||
profile_ex.c.version == version,
|
||||
))
|
||||
sql = select(profile_ex).where(
|
||||
and_(
|
||||
profile_ex.c.user == aime_id,
|
||||
profile_ex.c.version == version,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
|
||||
def put_profile_option(self, aime_id: int, option_data: Dict) -> Optional[int]:
|
||||
option_data["user"] = aime_id
|
||||
|
||||
@@ -416,7 +478,9 @@ class ChuniProfileData(BaseData):
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_option: Failed to update! aime_id: {aime_id}")
|
||||
self.logger.warn(
|
||||
f"put_profile_option: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
@@ -424,18 +488,23 @@ class ChuniProfileData(BaseData):
|
||||
sql = select(option).where(option.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_profile_option_ex(self, aime_id: int, option_ex_data: Dict) -> Optional[int]:
|
||||
|
||||
def put_profile_option_ex(
|
||||
self, aime_id: int, option_ex_data: Dict
|
||||
) -> Optional[int]:
|
||||
option_ex_data["user"] = aime_id
|
||||
|
||||
|
||||
sql = insert(option_ex).values(**option_ex_data)
|
||||
conflict = sql.on_duplicate_key_update(**option_ex_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_option_ex: Failed to update! aime_id: {aime_id}")
|
||||
self.logger.warn(
|
||||
f"put_profile_option_ex: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
@@ -443,27 +512,32 @@ class ChuniProfileData(BaseData):
|
||||
sql = select(option_ex).where(option_ex.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_profile_recent_rating(self, aime_id: int, recent_rating_data: List[Dict]) -> Optional[int]:
|
||||
def put_profile_recent_rating(
|
||||
self, aime_id: int, recent_rating_data: List[Dict]
|
||||
) -> Optional[int]:
|
||||
sql = insert(recent_rating).values(
|
||||
user = aime_id,
|
||||
recentRating = recent_rating_data
|
||||
user=aime_id, recentRating=recent_rating_data
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(recentRating = recent_rating_data)
|
||||
conflict = sql.on_duplicate_key_update(recentRating=recent_rating_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_recent_rating: Failed to update! aime_id: {aime_id}")
|
||||
self.logger.warn(
|
||||
f"put_profile_recent_rating: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_profile_recent_rating(self, aime_id: int) -> Optional[Row]:
|
||||
sql = select(recent_rating).where(recent_rating.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_profile_activity(self, aime_id: int, activity_data: Dict) -> Optional[int]:
|
||||
@@ -471,35 +545,39 @@ class ChuniProfileData(BaseData):
|
||||
activity_data["user"] = aime_id
|
||||
activity_data["activityId"] = activity_data["id"]
|
||||
activity_data.pop("id")
|
||||
|
||||
|
||||
sql = insert(activity).values(**activity_data)
|
||||
conflict = sql.on_duplicate_key_update(**activity_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_activity: Failed to update! aime_id: {aime_id}")
|
||||
self.logger.warn(
|
||||
f"put_profile_activity: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_profile_activity(self, aime_id: int, kind: int) -> Optional[List[Row]]:
|
||||
sql = select(activity).where(and_(
|
||||
activity.c.user == aime_id,
|
||||
activity.c.kind == kind
|
||||
))
|
||||
sql = select(activity).where(
|
||||
and_(activity.c.user == aime_id, activity.c.kind == kind)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_profile_charge(self, aime_id: int, charge_data: Dict) -> Optional[int]:
|
||||
charge_data["user"] = aime_id
|
||||
|
||||
|
||||
sql = insert(charge).values(**charge_data)
|
||||
conflict = sql.on_duplicate_key_update(**charge_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_charge: Failed to update! aime_id: {aime_id}")
|
||||
self.logger.warn(
|
||||
f"put_profile_charge: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
@@ -507,9 +585,10 @@ class ChuniProfileData(BaseData):
|
||||
sql = select(charge).where(charge.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def add_profile_region(self, aime_id: int, region_id: int) -> Optional[int]:
|
||||
pass
|
||||
|
||||
@@ -523,29 +602,35 @@ class ChuniProfileData(BaseData):
|
||||
conflict = sql.on_duplicate_key_update(**emoney_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_profile_emoney(self, aime_id: int) -> Optional[List[Row]]:
|
||||
sql = select(emoney).where(emoney.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_profile_overpower(self, aime_id: int, overpower_data: Dict) -> Optional[int]:
|
||||
def put_profile_overpower(
|
||||
self, aime_id: int, overpower_data: Dict
|
||||
) -> Optional[int]:
|
||||
overpower_data["user"] = aime_id
|
||||
|
||||
sql = insert(overpower).values(**overpower_data)
|
||||
conflict = sql.on_duplicate_key_update(**overpower_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_profile_overpower(self, aime_id: int) -> Optional[List[Row]]:
|
||||
sql = select(overpower).where(overpower.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
@@ -13,7 +13,11 @@ course = Table(
|
||||
"chuni_score_course",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("courseId", Integer),
|
||||
Column("classId", Integer),
|
||||
Column("playCount", Integer),
|
||||
@@ -29,15 +33,22 @@ course = Table(
|
||||
Column("param3", Integer),
|
||||
Column("param4", Integer),
|
||||
Column("isClear", Boolean),
|
||||
Column("theoryCount", Integer),
|
||||
Column("orderId", Integer),
|
||||
Column("playerRating", Integer),
|
||||
UniqueConstraint("user", "courseId", name="chuni_score_course_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
best_score = Table(
|
||||
"chuni_score_best",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("musicId", Integer),
|
||||
Column("level", Integer),
|
||||
Column("playCount", Integer),
|
||||
@@ -57,14 +68,18 @@ best_score = Table(
|
||||
Column("ext1", Integer),
|
||||
Column("theoryCount", Integer),
|
||||
UniqueConstraint("user", "musicId", "level", name="chuni_score_best_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
playlog = Table(
|
||||
"chuni_score_playlog",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("orderId", Integer),
|
||||
Column("sortNumber", Integer),
|
||||
Column("placeId", Integer),
|
||||
@@ -119,15 +134,17 @@ playlog = Table(
|
||||
Column("charaIllustId", Integer),
|
||||
Column("romVersion", String(255)),
|
||||
Column("judgeHeaven", Integer),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class ChuniScoreData(BaseData):
|
||||
def get_courses(self, aime_id: int) -> Optional[Row]:
|
||||
sql = select(course).where(course.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_course(self, aime_id: int, course_data: Dict) -> Optional[int]:
|
||||
@@ -138,16 +155,18 @@ class ChuniScoreData(BaseData):
|
||||
conflict = sql.on_duplicate_key_update(**course_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_scores(self, aime_id: int) -> Optional[Row]:
|
||||
sql = select(best_score).where(best_score.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def put_score(self, aime_id: int, score_data: Dict) -> Optional[int]:
|
||||
score_data["user"] = aime_id
|
||||
score_data = self.fix_bools(score_data)
|
||||
@@ -156,16 +175,18 @@ class ChuniScoreData(BaseData):
|
||||
conflict = sql.on_duplicate_key_update(**score_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_playlogs(self, aime_id: int) -> Optional[Row]:
|
||||
sql = select(playlog).where(playlog.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def put_playlog(self, aime_id: int, playlog_data: Dict) -> Optional[int]:
|
||||
playlog_data["user"] = aime_id
|
||||
playlog_data = self.fix_bools(playlog_data)
|
||||
@@ -174,5 +195,6 @@ class ChuniScoreData(BaseData):
|
||||
conflict = sql.on_duplicate_key_update(**playlog_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
@@ -19,7 +19,7 @@ events = Table(
|
||||
Column("name", String(255)),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
UniqueConstraint("version", "eventId", name="chuni_static_events_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
music = Table(
|
||||
@@ -30,13 +30,13 @@ music = Table(
|
||||
Column("songId", Integer),
|
||||
Column("chartId", Integer),
|
||||
Column("title", String(255)),
|
||||
Column("artist", String(255)),
|
||||
Column("artist", String(255)),
|
||||
Column("level", Float),
|
||||
Column("genre", String(255)),
|
||||
Column("jacketPath", String(255)),
|
||||
Column("worldsEndTag", String(20)),
|
||||
Column("worldsEndTag", String(7)),
|
||||
UniqueConstraint("version", "songId", "chartId", name="chuni_static_music_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
charge = Table(
|
||||
@@ -51,7 +51,7 @@ charge = Table(
|
||||
Column("sellingAppeal", Boolean),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
UniqueConstraint("version", "chargeId", name="chuni_static_charge_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
avatar = Table(
|
||||
@@ -65,159 +65,203 @@ avatar = Table(
|
||||
Column("iconPath", String(255)),
|
||||
Column("texturePath", String(255)),
|
||||
UniqueConstraint("version", "avatarAccessoryId", name="chuni_static_avatar_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class ChuniStaticData(BaseData):
|
||||
def put_event(self, version: int, event_id: int, type: int, name: str) -> Optional[int]:
|
||||
def put_event(
|
||||
self, version: int, event_id: int, type: int, name: str
|
||||
) -> Optional[int]:
|
||||
sql = insert(events).values(
|
||||
version = version,
|
||||
eventId = event_id,
|
||||
type = type,
|
||||
name = name
|
||||
version=version, eventId=event_id, type=type, name=name
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
name = name
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(name=name)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def update_event(self, version: int, event_id: int, enabled: bool) -> Optional[bool]:
|
||||
sql = events.update(and_(events.c.version == version, events.c.eventId == event_id)).values(
|
||||
enabled = enabled
|
||||
)
|
||||
|
||||
def update_event(
|
||||
self, version: int, event_id: int, enabled: bool
|
||||
) -> Optional[bool]:
|
||||
sql = events.update(
|
||||
and_(events.c.version == version, events.c.eventId == event_id)
|
||||
).values(enabled=enabled)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
self.logger.warn(f"update_event: failed to update event! version: {version}, event_id: {event_id}, enabled: {enabled}")
|
||||
if result is None:
|
||||
self.logger.warn(
|
||||
f"update_event: failed to update event! version: {version}, event_id: {event_id}, enabled: {enabled}"
|
||||
)
|
||||
return None
|
||||
|
||||
event = self.get_event(version, event_id)
|
||||
if event is None:
|
||||
self.logger.warn(f"update_event: failed to fetch event {event_id} after updating")
|
||||
self.logger.warn(
|
||||
f"update_event: failed to fetch event {event_id} after updating"
|
||||
)
|
||||
return None
|
||||
return event["enabled"]
|
||||
|
||||
def get_event(self, version: int, event_id: int) -> Optional[Row]:
|
||||
sql = select(events).where(and_(events.c.version == version, events.c.eventId == event_id))
|
||||
|
||||
sql = select(events).where(
|
||||
and_(events.c.version == version, events.c.eventId == event_id)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_enabled_events(self, version: int) -> Optional[List[Row]]:
|
||||
sql = select(events).where(and_(events.c.version == version, events.c.enabled == True))
|
||||
sql = select(events).where(
|
||||
and_(events.c.version == version, events.c.enabled == True)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_events(self, version: int) -> Optional[List[Row]]:
|
||||
sql = select(events).where(events.c.version == version)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_music(self, version: int, song_id: int, chart_id: int, title: int, artist: str,
|
||||
level: float, genre: str, jacketPath: str, we_tag: str) -> Optional[int]:
|
||||
|
||||
def put_music(
|
||||
self,
|
||||
version: int,
|
||||
song_id: int,
|
||||
chart_id: int,
|
||||
title: int,
|
||||
artist: str,
|
||||
level: float,
|
||||
genre: str,
|
||||
jacketPath: str,
|
||||
we_tag: str,
|
||||
) -> Optional[int]:
|
||||
sql = insert(music).values(
|
||||
version = version,
|
||||
songId = song_id,
|
||||
chartId = chart_id,
|
||||
title = title,
|
||||
artist = artist,
|
||||
level = level,
|
||||
genre = genre,
|
||||
jacketPath = jacketPath,
|
||||
worldsEndTag = we_tag,
|
||||
version=version,
|
||||
songId=song_id,
|
||||
chartId=chart_id,
|
||||
title=title,
|
||||
artist=artist,
|
||||
level=level,
|
||||
genre=genre,
|
||||
jacketPath=jacketPath,
|
||||
worldsEndTag=we_tag,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
title = title,
|
||||
artist = artist,
|
||||
level = level,
|
||||
genre = genre,
|
||||
jacketPath = jacketPath,
|
||||
worldsEndTag = we_tag,
|
||||
title=title,
|
||||
artist=artist,
|
||||
level=level,
|
||||
genre=genre,
|
||||
jacketPath=jacketPath,
|
||||
worldsEndTag=we_tag,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def put_charge(self, version: int, charge_id: int, name: str, expiration_days: int,
|
||||
consume_type: int, selling_appeal: bool) -> Optional[int]:
|
||||
|
||||
def put_charge(
|
||||
self,
|
||||
version: int,
|
||||
charge_id: int,
|
||||
name: str,
|
||||
expiration_days: int,
|
||||
consume_type: int,
|
||||
selling_appeal: bool,
|
||||
) -> Optional[int]:
|
||||
sql = insert(charge).values(
|
||||
version = version,
|
||||
chargeId = charge_id,
|
||||
name = name,
|
||||
expirationDays = expiration_days,
|
||||
consumeType = consume_type,
|
||||
sellingAppeal = selling_appeal,
|
||||
version=version,
|
||||
chargeId=charge_id,
|
||||
name=name,
|
||||
expirationDays=expiration_days,
|
||||
consumeType=consume_type,
|
||||
sellingAppeal=selling_appeal,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
name = name,
|
||||
expirationDays = expiration_days,
|
||||
consumeType = consume_type,
|
||||
sellingAppeal = selling_appeal,
|
||||
name=name,
|
||||
expirationDays=expiration_days,
|
||||
consumeType=consume_type,
|
||||
sellingAppeal=selling_appeal,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_enabled_charges(self, version: int) -> Optional[List[Row]]:
|
||||
sql = select(charge).where(and_(
|
||||
charge.c.version == version,
|
||||
charge.c.enabled == True
|
||||
))
|
||||
sql = select(charge).where(
|
||||
and_(charge.c.version == version, charge.c.enabled == True)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def get_charges(self, version: int) -> Optional[List[Row]]:
|
||||
sql = select(charge).where(charge.c.version == version)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_music_chart(self, version: int, song_id: int, chart_id: int) -> Optional[List[Row]]:
|
||||
sql = select(music).where(and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
music.c.chartId == chart_id
|
||||
))
|
||||
def get_music_chart(
|
||||
self, version: int, song_id: int, chart_id: int
|
||||
) -> Optional[List[Row]]:
|
||||
sql = select(music).where(
|
||||
and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
music.c.chartId == chart_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_avatar(self, version: int, avatarAccessoryId: int, name: str, category: int, iconPath: str, texturePath: str) -> Optional[int]:
|
||||
def put_avatar(
|
||||
self,
|
||||
version: int,
|
||||
avatarAccessoryId: int,
|
||||
name: str,
|
||||
category: int,
|
||||
iconPath: str,
|
||||
texturePath: str,
|
||||
) -> Optional[int]:
|
||||
sql = insert(avatar).values(
|
||||
version = version,
|
||||
avatarAccessoryId = avatarAccessoryId,
|
||||
name = name,
|
||||
category = category,
|
||||
iconPath = iconPath,
|
||||
texturePath = texturePath,
|
||||
version=version,
|
||||
avatarAccessoryId=avatarAccessoryId,
|
||||
name=name,
|
||||
category=category,
|
||||
iconPath=iconPath,
|
||||
texturePath=texturePath,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
name = name,
|
||||
category = category,
|
||||
iconPath = iconPath,
|
||||
texturePath = texturePath,
|
||||
name=name,
|
||||
category=category,
|
||||
iconPath=iconPath,
|
||||
texturePath=texturePath,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
@@ -5,12 +5,13 @@ from titles.chuni.base import ChuniBase
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniStar(ChuniBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_STAR
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.20.00"
|
||||
return ret
|
||||
return ret
|
||||
|
||||
@@ -5,12 +5,13 @@ from titles.chuni.base import ChuniBase
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniStarPlus(ChuniBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_STAR_PLUS
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.25.00"
|
||||
return ret
|
||||
return ret
|
||||
|
||||
10
titles/cm/__init__.py
Normal file
10
titles/cm/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from titles.cm.index import CardMakerServlet
|
||||
from titles.cm.const import CardMakerConstants
|
||||
from titles.cm.read import CardMakerReader
|
||||
|
||||
index = CardMakerServlet
|
||||
reader = CardMakerReader
|
||||
|
||||
game_codes = [CardMakerConstants.GAME_CODE]
|
||||
|
||||
current_schema_version = 1
|
||||
77
titles/cm/base.py
Normal file
77
titles/cm/base.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from datetime import date, datetime, timedelta
|
||||
from typing import Any, Dict, List
|
||||
import json
|
||||
import logging
|
||||
from enum import Enum
|
||||
|
||||
from core.config import CoreConfig
|
||||
from core.data.cache import cached
|
||||
from titles.cm.const import CardMakerConstants
|
||||
from titles.cm.config import CardMakerConfig
|
||||
|
||||
|
||||
class CardMakerBase:
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: CardMakerConfig) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.game_cfg = game_cfg
|
||||
self.date_time_format = "%Y-%m-%d %H:%M:%S"
|
||||
self.date_time_format_ext = (
|
||||
"%Y-%m-%d %H:%M:%S.%f" # needs to be lopped off at [:-5]
|
||||
)
|
||||
self.date_time_format_short = "%Y-%m-%d"
|
||||
self.logger = logging.getLogger("cardmaker")
|
||||
self.game = CardMakerConstants.GAME_CODE
|
||||
self.version = CardMakerConstants.VER_CARD_MAKER
|
||||
|
||||
def handle_get_game_connect_api_request(self, data: Dict) -> Dict:
|
||||
if self.core_cfg.server.is_develop:
|
||||
uri = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}"
|
||||
else:
|
||||
uri = f"http://{self.core_cfg.title.hostname}"
|
||||
|
||||
# CHUNITHM = 0, maimai = 1, ONGEKI = 2
|
||||
return {
|
||||
"length": 3,
|
||||
"gameConnectList": [
|
||||
{"modelKind": 0, "type": 1, "titleUri": f"{uri}/SDHD/200/"},
|
||||
{"modelKind": 1, "type": 1, "titleUri": f"{uri}/SDEZ/120/"},
|
||||
{"modelKind": 2, "type": 1, "titleUri": f"{uri}/SDDT/130/"},
|
||||
],
|
||||
}
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
reboot_start = date.strftime(
|
||||
datetime.now() + timedelta(hours=3), self.date_time_format
|
||||
)
|
||||
reboot_end = date.strftime(
|
||||
datetime.now() + timedelta(hours=4), self.date_time_format
|
||||
)
|
||||
|
||||
return {
|
||||
"gameSetting": {
|
||||
"dataVersion": "1.30.00",
|
||||
"ongekiCmVersion": "1.30.01",
|
||||
"chuniCmVersion": "2.00.00",
|
||||
"maimaiCmVersion": "1.20.00",
|
||||
"requestInterval": 10,
|
||||
"rebootStartTime": reboot_start,
|
||||
"rebootEndTime": reboot_end,
|
||||
"maxCountCharacter": 100,
|
||||
"maxCountItem": 100,
|
||||
"maxCountCard": 100,
|
||||
"watermark": False,
|
||||
"isMaintenance": False,
|
||||
"isBackgroundDistribute": False,
|
||||
},
|
||||
"isDumpUpload": False,
|
||||
"isAou": False,
|
||||
}
|
||||
|
||||
def handle_get_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||
return {"placeId": data["placeId"], "length": 0, "clientBookkeepingList": []}
|
||||
|
||||
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
||||
|
||||
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "UpsertClientBookkeepingApi"}
|
||||
36
titles/cm/cm136.py
Normal file
36
titles/cm/cm136.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from datetime import date, datetime, timedelta
|
||||
from typing import Any, Dict, List
|
||||
import json
|
||||
import logging
|
||||
from enum import Enum
|
||||
|
||||
from core.config import CoreConfig
|
||||
from core.data.cache import cached
|
||||
from titles.cm.base import CardMakerBase
|
||||
from titles.cm.const import CardMakerConstants
|
||||
from titles.cm.config import CardMakerConfig
|
||||
|
||||
|
||||
class CardMaker136(CardMakerBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: CardMakerConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = CardMakerConstants.VER_CARD_MAKER_136
|
||||
|
||||
def handle_get_game_connect_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_connect_api_request(data)
|
||||
if self.core_cfg.server.is_develop:
|
||||
uri = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}"
|
||||
else:
|
||||
uri = f"http://{self.core_cfg.title.hostname}"
|
||||
|
||||
ret["gameConnectList"][0]["titleUri"] = f"{uri}/SDHD/205/"
|
||||
ret["gameConnectList"][1]["titleUri"] = f"{uri}/SDEZ/125/"
|
||||
ret["gameConnectList"][2]["titleUri"] = f"{uri}/SDDT/135/"
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
||||
ret["gameSetting"]["ongekiCmVersion"] = "1.35.04"
|
||||
ret["gameSetting"]["chuniCmVersion"] = "2.05.00"
|
||||
ret["gameSetting"]["maimaiCmVersion"] = "1.25.00"
|
||||
return ret
|
||||
501
titles/cm/cm_data/MU3/static_gacha_cards.csv
Normal file
501
titles/cm/cm_data/MU3/static_gacha_cards.csv
Normal file
@@ -0,0 +1,501 @@
|
||||
"gachaId","cardId","rarity","weight","isPickup","isSelect"
|
||||
1070,100984,4,1,0,1
|
||||
1070,100997,3,2,0,1
|
||||
1070,100998,3,2,0,1
|
||||
1070,101020,2,3,0,1
|
||||
1070,101021,2,3,0,1
|
||||
1070,101022,2,3,0,1
|
||||
1067,100982,4,1,0,0
|
||||
1067,100983,4,1,0,0
|
||||
1067,100996,3,2,0,0
|
||||
1068,100075,2,3,0,0
|
||||
1068,100182,2,3,0,0
|
||||
1068,100348,2,3,0,0
|
||||
1068,100232,2,3,0,0
|
||||
1068,100417,2,3,0,0
|
||||
1068,100755,2,3,0,0
|
||||
1068,100077,3,2,0,0
|
||||
1068,100271,3,2,0,0
|
||||
1068,100425,3,2,0,0
|
||||
1068,100758,3,2,0,0
|
||||
1068,101000,3,2,0,0
|
||||
1068,100284,4,1,0,0
|
||||
1068,100767,4,1,0,0
|
||||
1068,101293,4,1,0,0
|
||||
1069,100069,2,3,0,0
|
||||
1069,100183,2,3,0,0
|
||||
1069,100349,2,3,0,0
|
||||
1069,100233,2,3,0,0
|
||||
1069,100416,2,3,0,0
|
||||
1069,100071,3,2,0,0
|
||||
1069,100272,3,2,0,0
|
||||
1069,100427,3,2,0,0
|
||||
1069,100805,3,2,0,0
|
||||
1069,101300,3,2,0,0
|
||||
1069,100285,4,1,0,0
|
||||
1069,100768,4,1,0,0
|
||||
1069,100988,4,1,0,0
|
||||
1071,100275,4,1,0,0
|
||||
1071,100437,4,1,0,0
|
||||
1071,100780,4,1,0,0
|
||||
1071,100006,3,2,0,0
|
||||
1071,100007,3,2,0,0
|
||||
1071,100249,3,2,0,0
|
||||
1071,100262,3,2,0,0
|
||||
1071,100418,3,2,0,0
|
||||
1071,100003,2,3,0,0
|
||||
1071,100004,2,3,0,0
|
||||
1071,100173,2,3,0,0
|
||||
1071,100223,2,3,0,0
|
||||
1071,100339,2,3,0,0
|
||||
1071,100692,2,3,0,0
|
||||
1072,100017,4,1,0,0
|
||||
1072,100276,4,1,0,0
|
||||
1072,100760,4,1,0,0
|
||||
1072,100015,3,2,0,0
|
||||
1072,100016,3,2,0,0
|
||||
1072,100250,3,2,0,0
|
||||
1072,100263,3,2,0,0
|
||||
1072,100423,3,2,0,0
|
||||
1072,100765,3,2,0,0
|
||||
1072,100012,2,3,0,0
|
||||
1072,100013,2,3,0,0
|
||||
1072,100174,2,3,0,0
|
||||
1072,100224,2,3,0,0
|
||||
1072,100340,2,3,0,0
|
||||
1072,100693,2,3,0,0
|
||||
1073,100026,4,1,0,0
|
||||
1073,100277,4,1,0,0
|
||||
1073,100761,4,1,0,0
|
||||
1073,100024,3,2,0,0
|
||||
1073,100025,3,2,0,0
|
||||
1073,100251,3,2,0,0
|
||||
1073,100264,3,2,0,0
|
||||
1073,100430,3,2,0,0
|
||||
1073,100021,2,3,0,0
|
||||
1073,100022,2,3,0,0
|
||||
1073,100175,2,3,0,0
|
||||
1073,100225,2,3,0,0
|
||||
1073,100341,2,3,0,0
|
||||
1073,100694,2,3,0,0
|
||||
1011,100454,4,1,0,0
|
||||
1011,100980,4,1,0,0
|
||||
1011,101553,4,1,0,0
|
||||
1011,100253,3,1,0,0
|
||||
1011,100241,3,1,0,0
|
||||
1011,100240,3,1,0,0
|
||||
1011,100239,3,1,0,0
|
||||
1011,100238,3,1,0,0
|
||||
1011,100237,3,1,0,0
|
||||
1011,100236,3,1,0,0
|
||||
1011,100261,3,1,0,0
|
||||
1011,100246,3,1,0,0
|
||||
1011,100245,3,1,0,0
|
||||
1011,100242,3,1,0,0
|
||||
1011,100243,3,1,0,0
|
||||
1011,100254,3,1,0,0
|
||||
1011,100338,3,1,0,0
|
||||
1011,100337,3,1,0,0
|
||||
1011,100336,3,1,0,0
|
||||
1011,100248,3,1,0,0
|
||||
1011,100247,3,1,0,0
|
||||
1011,100244,3,1,0,0
|
||||
1011,100259,3,1,0,0
|
||||
1011,100257,3,1,0,0
|
||||
1011,100258,3,1,0,0
|
||||
1011,100636,3,1,0,0
|
||||
1011,100634,3,1,0,0
|
||||
1011,100255,3,1,0,0
|
||||
1011,100256,3,1,0,0
|
||||
1011,100252,3,1,0,0
|
||||
1011,100638,3,1,0,0
|
||||
1011,100639,3,1,0,0
|
||||
1011,100637,3,1,0,0
|
||||
1011,100772,3,1,0,0
|
||||
1011,100667,3,1,0,0
|
||||
1011,100666,3,1,0,0
|
||||
1011,100665,3,1,0,0
|
||||
1011,100643,3,1,0,0
|
||||
1011,100640,3,1,0,0
|
||||
1011,100641,3,1,0,0
|
||||
1011,100642,3,1,0,0
|
||||
1011,100688,3,1,0,0
|
||||
1011,100645,3,1,0,0
|
||||
1011,100646,3,1,0,0
|
||||
1011,100644,3,1,0,0
|
||||
1012,100644,3,1,0,0
|
||||
1012,100646,3,1,0,0
|
||||
1012,100645,3,1,0,0
|
||||
1012,100688,3,1,0,0
|
||||
1012,100642,3,1,0,0
|
||||
1012,100641,3,1,0,0
|
||||
1012,100640,3,1,0,0
|
||||
1012,100643,3,1,0,0
|
||||
1012,100665,3,1,0,0
|
||||
1012,100666,3,1,0,0
|
||||
1012,100667,3,1,0,0
|
||||
1012,100634,3,1,0,0
|
||||
1012,100636,3,1,0,0
|
||||
1012,100772,3,1,0,0
|
||||
1012,100638,3,1,0,0
|
||||
1012,100637,3,1,0,0
|
||||
1012,100639,3,1,0,0
|
||||
1012,100252,3,1,0,0
|
||||
1012,100256,3,1,0,0
|
||||
1012,100255,3,1,0,0
|
||||
1012,100258,3,1,0,0
|
||||
1012,100257,3,1,0,0
|
||||
1012,100259,3,1,0,0
|
||||
1012,100244,3,1,0,0
|
||||
1012,100247,3,1,0,0
|
||||
1012,100248,3,1,0,0
|
||||
1012,100336,3,1,0,0
|
||||
1012,100337,3,1,0,0
|
||||
1012,100338,3,1,0,0
|
||||
1012,100254,3,1,0,0
|
||||
1012,100243,3,1,0,0
|
||||
1012,100242,3,1,0,0
|
||||
1012,100245,3,1,0,0
|
||||
1012,100246,3,1,0,0
|
||||
1012,100261,3,1,0,0
|
||||
1012,100236,3,1,0,0
|
||||
1012,100237,3,1,0,0
|
||||
1012,100238,3,1,0,0
|
||||
1012,100239,3,1,0,0
|
||||
1012,100240,3,1,0,0
|
||||
1012,100241,3,1,0,0
|
||||
1012,100253,3,1,0,0
|
||||
1012,100454,4,1,0,0
|
||||
1012,100980,4,1,0,0
|
||||
1012,101553,4,1,0,0
|
||||
1074,100985,4,1,0,0
|
||||
1074,100999,3,1,0,0
|
||||
1074,101000,3,1,0,0
|
||||
1074,101023,2,1,0,0
|
||||
1074,101024,2,1,0,0
|
||||
1075,100060,4,1,0,0
|
||||
1075,100434,4,1,0,0
|
||||
1075,100059,3,1,0,0
|
||||
1075,100268,3,1,0,0
|
||||
1075,100420,3,1,0,0
|
||||
1075,100763,3,1,0,0
|
||||
1075,101003,3,1,0,0
|
||||
1075,100057,2,1,0,0
|
||||
1075,100179,2,1,0,0
|
||||
1075,100229,2,1,0,0
|
||||
1075,100345,2,1,0,0
|
||||
1075,100415,2,1,0,0
|
||||
1076,100054,4,1,0,0
|
||||
1076,100282,4,1,0,0
|
||||
1076,100726,4,1,0,0
|
||||
1076,100053,3,1,0,0
|
||||
1076,100269,3,1,0,0
|
||||
1076,100422,3,1,0,0
|
||||
1076,100757,3,1,0,0
|
||||
1076,100051,2,1,0,0
|
||||
1076,100180,2,1,0,0
|
||||
1076,100230,2,1,0,0
|
||||
1076,100346,2,1,0,0
|
||||
1076,100414,2,1,0,0
|
||||
1077,100984,4,1,0,1
|
||||
1077,100997,3,1,0,1
|
||||
1077,100998,3,1,0,1
|
||||
1077,100986,4,1,0,1
|
||||
1077,101001,3,1,0,1
|
||||
1077,101002,3,1,0,1
|
||||
1077,101025,2,1,0,1
|
||||
1077,101026,2,1,0,1
|
||||
1077,101027,2,1,0,1
|
||||
1081,100987,4,1,0,0
|
||||
1081,100988,4,1,0,0
|
||||
1081,101003,3,1,0,0
|
||||
1085,100008,4,1,0,1
|
||||
1085,100017,4,1,0,1
|
||||
1085,100026,4,1,0,1
|
||||
1085,100034,4,1,0,1
|
||||
1085,100041,4,1,0,1
|
||||
1085,100048,4,1,0,1
|
||||
1085,100054,4,1,0,1
|
||||
1085,100060,4,1,0,1
|
||||
1085,100066,4,1,0,1
|
||||
1085,100078,4,1,0,1
|
||||
1085,100072,4,1,0,1
|
||||
1085,100084,4,1,0,1
|
||||
1085,100090,4,1,0,1
|
||||
1085,100282,4,1,0,1
|
||||
1085,100285,4,1,0,1
|
||||
1085,100284,4,1,0,1
|
||||
1085,100286,4,1,0,1
|
||||
1085,100280,4,1,0,1
|
||||
1085,100276,4,1,0,1
|
||||
1085,100277,4,1,0,1
|
||||
1085,100275,4,1,0,1
|
||||
1085,100278,4,1,0,1
|
||||
1085,100431,4,1,0,1
|
||||
1085,100407,4,1,0,1
|
||||
1085,100432,4,1,0,1
|
||||
1085,100433,4,1,0,1
|
||||
1085,100434,4,1,0,1
|
||||
1085,100435,4,1,0,1
|
||||
1085,100436,4,1,0,1
|
||||
1085,100437,4,1,0,1
|
||||
1085,100438,4,1,0,1
|
||||
1085,100439,4,1,0,1
|
||||
1085,100760,4,1,0,1
|
||||
1085,100761,4,1,0,1
|
||||
1085,100779,4,1,0,1
|
||||
1085,100767,4,1,0,1
|
||||
1085,100780,4,1,0,1
|
||||
1085,100784,4,1,0,1
|
||||
1085,100768,4,1,0,1
|
||||
1085,100725,4,1,0,1
|
||||
1085,100726,4,1,0,1
|
||||
1085,100984,4,1,0,1
|
||||
1085,100985,4,1,0,1
|
||||
1085,100987,4,1,0,1
|
||||
1085,100988,4,1,0,1
|
||||
1085,100986,4,1,0,1
|
||||
1085,100989,4,1,0,1
|
||||
1085,100982,4,1,0,1
|
||||
1085,100983,4,1,0,1
|
||||
1085,100787,4,1,0,1
|
||||
1085,101293,4,1,0,1
|
||||
1085,101294,4,1,0,1
|
||||
1085,101295,4,1,0,1
|
||||
1085,101296,4,1,0,1
|
||||
1085,101297,4,1,0,1
|
||||
1085,101320,4,1,0,1
|
||||
1085,101567,4,1,0,1
|
||||
1085,101592,4,1,0,1
|
||||
1085,101593,4,1,0,1
|
||||
1085,101594,4,1,0,1
|
||||
1085,101595,4,1,0,1
|
||||
1089,100989,4,1,0,0
|
||||
1089,101004,3,1,0,0
|
||||
1089,101005,3,1,0,0
|
||||
1104,101293,4,1,0,0
|
||||
1104,101294,4,1,0,0
|
||||
1104,101298,3,1,0,0
|
||||
1111,100008,4,1,0,1
|
||||
1111,100017,4,1,0,1
|
||||
1111,100026,4,1,0,1
|
||||
1111,100034,4,1,0,1
|
||||
1111,100041,4,1,0,1
|
||||
1111,100048,4,1,0,1
|
||||
1111,100054,4,1,0,1
|
||||
1111,100060,4,1,0,1
|
||||
1111,100066,4,1,0,1
|
||||
1111,100078,4,1,0,1
|
||||
1111,100072,4,1,0,1
|
||||
1111,100084,4,1,0,1
|
||||
1111,100090,4,1,0,1
|
||||
1111,100282,4,1,0,1
|
||||
1111,100285,4,1,0,1
|
||||
1111,100284,4,1,0,1
|
||||
1111,100286,4,1,0,1
|
||||
1111,100280,4,1,0,1
|
||||
1111,100276,4,1,0,1
|
||||
1111,100277,4,1,0,1
|
||||
1111,100275,4,1,0,1
|
||||
1111,100278,4,1,0,1
|
||||
1111,100431,4,1,0,1
|
||||
1111,100407,4,1,0,1
|
||||
1111,100432,4,1,0,1
|
||||
1111,100433,4,1,0,1
|
||||
1111,100434,4,1,1,1
|
||||
1111,100435,4,1,1,1
|
||||
1111,100436,4,1,0,1
|
||||
1111,100437,4,1,0,1
|
||||
1111,100438,4,1,0,1
|
||||
1111,100439,4,1,0,1
|
||||
1111,100760,4,1,1,1
|
||||
1111,100761,4,1,0,1
|
||||
1111,100779,4,1,0,1
|
||||
1111,100767,4,1,0,1
|
||||
1111,100780,4,1,1,1
|
||||
1111,100784,4,1,1,1
|
||||
1111,100768,4,1,0,1
|
||||
1111,100725,4,1,1,1
|
||||
1111,100726,4,1,1,1
|
||||
1111,100985,4,1,1,1
|
||||
1111,100988,4,1,1,1
|
||||
1111,100989,4,1,1,1
|
||||
1111,100982,4,1,1,1
|
||||
1111,100983,4,1,1,1
|
||||
1111,101293,4,1,1,1
|
||||
1111,101294,4,1,1,1
|
||||
1111,101295,4,1,1,1
|
||||
1111,101320,4,1,1,1
|
||||
1135,101567,4,1,0,0
|
||||
1135,101592,4,1,0,0
|
||||
1135,101594,4,1,0,0
|
||||
1135,101595,4,1,0,0
|
||||
1135,101566,3,1,0,0
|
||||
1135,101602,3,1,0,0
|
||||
1135,101603,3,1,0,0
|
||||
1135,101619,2,1,0,0
|
||||
1156,101604,3,1,0,0
|
||||
1156,101605,3,1,0,0
|
||||
1156,101607,3,1,0,0
|
||||
1156,101608,3,1,0,0
|
||||
1156,101596,4,1,0,0
|
||||
1156,101597,4,1,0,0
|
||||
1156,101599,4,1,0,0
|
||||
1156,101600,4,1,0,0
|
||||
1149,100003,2,1,0,0
|
||||
1149,100004,2,1,0,0
|
||||
1149,100012,2,1,0,0
|
||||
1149,100013,2,1,0,0
|
||||
1149,100021,2,1,0,0
|
||||
1149,100022,2,1,0,0
|
||||
1149,100173,2,1,0,0
|
||||
1149,100174,2,1,0,0
|
||||
1149,100175,2,1,0,0
|
||||
1149,100339,2,1,0,0
|
||||
1149,100340,2,1,0,0
|
||||
1149,100341,2,1,0,0
|
||||
1149,100223,2,1,0,0
|
||||
1149,100224,2,1,0,0
|
||||
1149,100225,2,1,0,0
|
||||
1149,100692,2,1,0,0
|
||||
1149,100693,2,1,0,0
|
||||
1149,100694,2,1,0,0
|
||||
1149,101020,2,1,0,0
|
||||
1149,101025,2,1,0,0
|
||||
1149,100418,3,1,0,0
|
||||
1149,101005,3,1,0,0
|
||||
1149,100785,3,1,0,0
|
||||
1149,100786,3,1,0,0
|
||||
1149,101602,3,1,0,0
|
||||
1149,101604,3,1,0,0
|
||||
1149,100760,4,1,0,0
|
||||
1149,100780,4,1,0,0
|
||||
1149,100987,4,1,0,0
|
||||
1149,101295,4,1,0,0
|
||||
1149,101296,4,1,0,0
|
||||
1149,101592,4,1,0,0
|
||||
1163,100008,4,1,0,1
|
||||
1163,100017,4,1,0,1
|
||||
1163,100026,4,1,0,1
|
||||
1163,100034,4,1,0,1
|
||||
1163,100041,4,1,0,1
|
||||
1163,100048,4,1,0,1
|
||||
1163,100054,4,1,0,1
|
||||
1163,100060,4,1,0,1
|
||||
1163,100066,4,1,0,1
|
||||
1163,100078,4,1,0,1
|
||||
1163,100072,4,1,0,1
|
||||
1163,100084,4,1,0,1
|
||||
1163,100090,4,1,0,1
|
||||
1163,100282,4,1,0,1
|
||||
1163,100285,4,1,0,1
|
||||
1163,100284,4,1,0,1
|
||||
1163,100286,4,1,0,1
|
||||
1163,100280,4,1,0,1
|
||||
1163,100276,4,1,0,1
|
||||
1163,100277,4,1,0,1
|
||||
1163,100275,4,1,0,1
|
||||
1163,100278,4,1,0,1
|
||||
1163,100431,4,1,0,1
|
||||
1163,100407,4,1,0,1
|
||||
1163,100432,4,1,0,1
|
||||
1163,100433,4,1,0,1
|
||||
1163,100434,4,1,0,1
|
||||
1163,100435,4,1,0,1
|
||||
1163,100436,4,1,0,1
|
||||
1163,100437,4,1,0,1
|
||||
1163,100438,4,1,0,1
|
||||
1163,100439,4,1,0,1
|
||||
1163,100760,4,1,0,1
|
||||
1163,100761,4,1,0,1
|
||||
1163,100779,4,1,0,1
|
||||
1163,100767,4,1,0,1
|
||||
1163,100780,4,1,0,1
|
||||
1163,100784,4,1,0,1
|
||||
1163,100768,4,1,0,1
|
||||
1163,100725,4,1,0,1
|
||||
1163,100726,4,1,0,1
|
||||
1163,100984,4,1,0,1
|
||||
1163,100985,4,1,0,1
|
||||
1163,100987,4,1,0,1
|
||||
1163,100988,4,1,0,1
|
||||
1163,100986,4,1,0,1
|
||||
1163,100989,4,1,0,1
|
||||
1163,100982,4,1,0,1
|
||||
1163,100983,4,1,0,1
|
||||
1163,100787,4,1,0,1
|
||||
1163,101293,4,1,0,1
|
||||
1163,101294,4,1,0,1
|
||||
1163,101295,4,1,0,1
|
||||
1163,101296,4,1,0,1
|
||||
1163,101297,4,1,0,1
|
||||
1163,101320,4,1,0,1
|
||||
1163,101567,4,1,0,1
|
||||
1164,100008,4,1,0,1
|
||||
1164,100017,4,1,0,1
|
||||
1164,100026,4,1,0,1
|
||||
1164,100034,4,1,0,1
|
||||
1164,100041,4,1,0,1
|
||||
1164,100048,4,1,0,1
|
||||
1164,100054,4,1,0,1
|
||||
1164,100060,4,1,0,1
|
||||
1164,100066,4,1,0,1
|
||||
1164,100078,4,1,0,1
|
||||
1164,100072,4,1,0,1
|
||||
1164,100084,4,1,0,1
|
||||
1164,100090,4,1,0,1
|
||||
1164,100282,4,1,0,1
|
||||
1164,100285,4,1,0,1
|
||||
1164,100284,4,1,0,1
|
||||
1164,100286,4,1,0,1
|
||||
1164,100280,4,1,0,1
|
||||
1164,100276,4,1,0,1
|
||||
1164,100277,4,1,0,1
|
||||
1164,100275,4,1,0,1
|
||||
1164,100278,4,1,0,1
|
||||
1164,100431,4,1,0,1
|
||||
1164,100407,4,1,0,1
|
||||
1164,100432,4,1,0,1
|
||||
1164,100433,4,1,0,1
|
||||
1164,100434,4,1,0,1
|
||||
1164,100435,4,1,0,1
|
||||
1164,100436,4,1,0,1
|
||||
1164,100437,4,1,0,1
|
||||
1164,100438,4,1,0,1
|
||||
1164,100439,4,1,0,1
|
||||
1164,100760,4,1,0,1
|
||||
1164,100761,4,1,0,1
|
||||
1164,100779,4,1,0,1
|
||||
1164,100767,4,1,0,1
|
||||
1164,100780,4,1,0,1
|
||||
1164,100784,4,1,0,1
|
||||
1164,100768,4,1,0,1
|
||||
1164,100725,4,1,0,1
|
||||
1164,100726,4,1,0,1
|
||||
1164,100984,4,1,0,1
|
||||
1164,100985,4,1,0,1
|
||||
1164,100987,4,1,0,1
|
||||
1164,100988,4,1,0,1
|
||||
1164,100986,4,1,0,1
|
||||
1164,100989,4,1,0,1
|
||||
1164,100982,4,1,0,1
|
||||
1164,100983,4,1,0,1
|
||||
1164,100787,4,1,0,1
|
||||
1164,101293,4,1,0,1
|
||||
1164,101294,4,1,0,1
|
||||
1164,101295,4,1,0,1
|
||||
1164,101296,4,1,0,1
|
||||
1164,101297,4,1,0,1
|
||||
1164,101320,4,1,0,1
|
||||
1164,101567,4,1,0,1
|
||||
1164,101592,4,1,0,1
|
||||
1164,101593,4,1,0,1
|
||||
1164,101594,4,1,0,1
|
||||
1164,101595,4,1,0,1
|
||||
1164,101598,4,1,0,1
|
||||
1164,101596,4,1,0,1
|
||||
1164,101597,4,1,0,1
|
||||
1164,101599,4,1,0,1
|
||||
1164,101600,4,1,0,1
|
||||
1141,101600,4,1,0,1
|
||||
1141,101608,3,1,0,1
|
||||
|
69
titles/cm/cm_data/MU3/static_gachas.csv
Normal file
69
titles/cm/cm_data/MU3/static_gachas.csv
Normal file
@@ -0,0 +1,69 @@
|
||||
"version","gachaId","gachaName","type","kind","isCeiling","maxSelectPoint"
|
||||
6,1011,"無料ガチャ",0,3,0,0
|
||||
6,1012,"無料ガチャ(SR確定)",0,3,0,0
|
||||
6,1043,"レギュラーガチャ",0,0,0,0
|
||||
6,1067,"例えるなら大人のパッションフルーツ
|
||||
リゾートプールガチャ",0,1,0,0
|
||||
6,1068,"柏木 咲姫
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1069,"井之原 小星
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1070,"目指すは優勝!
|
||||
炎の体育祭リミテッドガチャ",0,1,1,110
|
||||
6,1071,"星咲 あかり
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1072,"藤沢 柚子
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1073,"三角 葵
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1074,"おくれてきた
|
||||
Halloweenガチャ",0,1,0,0
|
||||
6,1075,"早乙女 彩華
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1076,"桜井 春菜
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1077,"ふわふわすぺーす
|
||||
お仕事体験リミテッドガチャ",0,1,1,110
|
||||
6,1078,"高瀬 梨緒
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1079,"結城 莉玖
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1080,"藍原 椿
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1081,"今夜はおうちでパーティ☆
|
||||
メリクリガチャ",0,1,0,0
|
||||
6,1082,"日向 千夏
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1083,"柏木 美亜
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1084,"東雲 つむぎ
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1085,"謹賀新年
|
||||
福袋ガチャ",0,0,1,33
|
||||
6,1086,"逢坂 茜
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1087,"珠洲島 有栖
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1088,"九條 楓
|
||||
ピックアップガチャ",0,2,0,0
|
||||
6,1089,"冬の魔法
|
||||
スーパーウルトラウィンターガチャ",0,1,0,0
|
||||
6,1093,"高瀬 梨緒ピックアップガチャ",0,2,0,0
|
||||
6,1094,"結城 莉玖ピックアップガチャ",0,2,0,0
|
||||
6,1095,"藍原 椿ピックアップガチャ",0,2,0,0
|
||||
6,1096,"早乙女 彩華ピックアップガチャ",0,2,0,0
|
||||
6,1097,"桜井 春菜ピックアップガチャ",0,2,0,0
|
||||
6,1098,"逢坂 茜ピックアップガチャ",0,2,0,0
|
||||
6,1099,"九條 楓ピックアップガチャ",0,2,0,0
|
||||
6,1100,"珠洲島 有栖ピックアップガチャ",0,2,0,0
|
||||
6,1101,"LEAF属性オンリーガチャ",0,2,0,0
|
||||
6,1102,"AQUA属性オンリーガチャ",0,2,0,0
|
||||
6,1103,"FIRE属性オンリーガチャ",0,2,0,0
|
||||
6,1104,"夜明け前の双星ガチャ",0,1,0,0
|
||||
6,1105,"謎の洞窟 黄金は実在した!!ガチャ",0,1,0,0
|
||||
6,1106,"スウィートブライダルリミテッドガチャ",0,1,0,0
|
||||
6,1107,"忘れられない、愛(ピュア)とロックがここにある。ガチャ",0,1,0,0
|
||||
6,1108,"メルティ夜ふかしガチャ",0,1,0,0
|
||||
6,1109,"絵本の国のシューターズガチャ",0,1,0,0
|
||||
6,1110,"オンゲキ R.E.D. PLUS 大感謝祭ガチャ",0,1,0,0
|
||||
6,1111,"オンゲキ 3rd Anniversaryガチャ",0,1,1,33
|
||||
|
25
titles/cm/config.py
Normal file
25
titles/cm/config.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from core.config import CoreConfig
|
||||
|
||||
|
||||
class CardMakerServerConfig:
|
||||
def __init__(self, parent_config: "CardMakerConfig") -> None:
|
||||
self.__config = parent_config
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "cardmaker", "server", "enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def loglevel(self) -> int:
|
||||
return CoreConfig.str_to_loglevel(
|
||||
CoreConfig.get_config_field(
|
||||
self.__config, "cardmaker", "server", "loglevel", default="info"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class CardMakerConfig(dict):
|
||||
def __init__(self) -> None:
|
||||
self.server = CardMakerServerConfig(self)
|
||||
13
titles/cm/const.py
Normal file
13
titles/cm/const.py
Normal file
@@ -0,0 +1,13 @@
|
||||
class CardMakerConstants:
|
||||
GAME_CODE = "SDED"
|
||||
|
||||
CONFIG_NAME = "cardmaker.yaml"
|
||||
|
||||
VER_CARD_MAKER = 0
|
||||
VER_CARD_MAKER_136 = 1
|
||||
|
||||
VERSION_NAMES = ("Card Maker 1.34", "Card Maker 1.36")
|
||||
|
||||
@classmethod
|
||||
def game_ver_to_string(cls, ver: int):
|
||||
return cls.VERSION_NAMES[ver]
|
||||
131
titles/cm/index.py
Normal file
131
titles/cm/index.py
Normal file
@@ -0,0 +1,131 @@
|
||||
import json
|
||||
import inflection
|
||||
import yaml
|
||||
import string
|
||||
import logging
|
||||
import coloredlogs
|
||||
import zlib
|
||||
|
||||
from os import path
|
||||
from typing import Tuple
|
||||
from twisted.web.http import Request
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.cm.config import CardMakerConfig
|
||||
from titles.cm.const import CardMakerConstants
|
||||
from titles.cm.base import CardMakerBase
|
||||
from titles.cm.cm136 import CardMaker136
|
||||
|
||||
|
||||
class CardMakerServlet:
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.game_cfg = CardMakerConfig()
|
||||
if path.exists(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"):
|
||||
self.game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
self.versions = [
|
||||
CardMakerBase(core_cfg, self.game_cfg),
|
||||
CardMaker136(core_cfg, self.game_cfg),
|
||||
]
|
||||
|
||||
self.logger = logging.getLogger("cardmaker")
|
||||
log_fmt_str = "[%(asctime)s] Card Maker | %(levelname)s | %(message)s"
|
||||
log_fmt = logging.Formatter(log_fmt_str)
|
||||
fileHandler = TimedRotatingFileHandler(
|
||||
"{0}/{1}.log".format(self.core_cfg.server.log_dir, "cardmaker"),
|
||||
encoding="utf8",
|
||||
when="d",
|
||||
backupCount=10,
|
||||
)
|
||||
|
||||
fileHandler.setFormatter(log_fmt)
|
||||
|
||||
consoleHandler = logging.StreamHandler()
|
||||
consoleHandler.setFormatter(log_fmt)
|
||||
|
||||
self.logger.addHandler(fileHandler)
|
||||
self.logger.addHandler(consoleHandler)
|
||||
|
||||
self.logger.setLevel(self.game_cfg.server.loglevel)
|
||||
coloredlogs.install(
|
||||
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_allnet_info(
|
||||
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
|
||||
) -> Tuple[bool, str, str]:
|
||||
game_cfg = CardMakerConfig()
|
||||
if path.exists(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"):
|
||||
game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
if not game_cfg.server.enable:
|
||||
return (False, "", "")
|
||||
|
||||
if core_cfg.server.is_develop:
|
||||
return (
|
||||
True,
|
||||
f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/",
|
||||
"",
|
||||
)
|
||||
|
||||
return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v/", "")
|
||||
|
||||
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
|
||||
req_raw = request.content.getvalue()
|
||||
url_split = url_path.split("/")
|
||||
internal_ver = 0
|
||||
endpoint = url_split[len(url_split) - 1]
|
||||
|
||||
print(f"version: {version}")
|
||||
|
||||
if version >= 130 and version < 135: # Card Maker
|
||||
internal_ver = CardMakerConstants.VER_CARD_MAKER
|
||||
elif version >= 135 and version < 140: # Card Maker
|
||||
internal_ver = CardMakerConstants.VER_CARD_MAKER_136
|
||||
|
||||
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
|
||||
# doing encrypted. The likelyhood of false positives is low but
|
||||
# technically not 0
|
||||
self.logger.error("Encryption not supported at this time")
|
||||
|
||||
try:
|
||||
unzip = zlib.decompress(req_raw)
|
||||
|
||||
except zlib.error as e:
|
||||
self.logger.error(
|
||||
f"Failed to decompress v{version} {endpoint} request -> {e}"
|
||||
)
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
|
||||
req_data = json.loads(unzip)
|
||||
|
||||
self.logger.info(f"v{version} {endpoint} request - {req_data}")
|
||||
|
||||
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
||||
|
||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||
return zlib.compress(b'{"returnCode": 1}')
|
||||
|
||||
try:
|
||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||
resp = handler(req_data)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
|
||||
if resp is None:
|
||||
resp = {"returnCode": 1}
|
||||
|
||||
self.logger.info(f"Response {resp}")
|
||||
|
||||
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||
155
titles/cm/read.py
Normal file
155
titles/cm/read.py
Normal file
@@ -0,0 +1,155 @@
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import csv
|
||||
import xml.etree.ElementTree as ET
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from read import BaseReader
|
||||
from core.config import CoreConfig
|
||||
from titles.ongeki.database import OngekiData
|
||||
from titles.cm.const import CardMakerConstants
|
||||
from titles.ongeki.const import OngekiConstants
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
|
||||
|
||||
class CardMakerReader(BaseReader):
|
||||
def __init__(
|
||||
self,
|
||||
config: CoreConfig,
|
||||
version: int,
|
||||
bin_dir: Optional[str],
|
||||
opt_dir: Optional[str],
|
||||
extra: Optional[str],
|
||||
) -> None:
|
||||
super().__init__(config, version, bin_dir, opt_dir, extra)
|
||||
self.ongeki_data = OngekiData(config)
|
||||
|
||||
try:
|
||||
self.logger.info(
|
||||
f"Start importer for {CardMakerConstants.game_ver_to_string(version)}"
|
||||
)
|
||||
except IndexError:
|
||||
self.logger.error(f"Invalid Card Maker version {version}")
|
||||
exit(1)
|
||||
|
||||
def read(self) -> None:
|
||||
static_datas = {
|
||||
"static_gachas.csv": "read_ongeki_gacha_csv",
|
||||
"static_gacha_cards.csv": "read_ongeki_gacha_card_csv",
|
||||
}
|
||||
|
||||
data_dirs = []
|
||||
|
||||
if self.bin_dir is not None:
|
||||
for file, func in static_datas.items():
|
||||
if os.path.exists(f"{self.bin_dir}/MU3/{file}"):
|
||||
read_csv = getattr(CardMakerReader, func)
|
||||
read_csv(self, f"{self.bin_dir}/MU3/{file}")
|
||||
else:
|
||||
self.logger.warn(
|
||||
f"Couldn't find {file} file in {self.bin_dir}, skipping"
|
||||
)
|
||||
|
||||
if self.opt_dir is not None:
|
||||
data_dirs += self.get_data_directories(self.opt_dir)
|
||||
|
||||
# ONGEKI (MU3) cnnot easily access the bin data(A000.pac)
|
||||
# so only opt_dir will work for now
|
||||
for dir in data_dirs:
|
||||
self.read_ongeki_gacha(f"{dir}/MU3/gacha")
|
||||
|
||||
def read_ongeki_gacha_csv(self, file_path: str) -> None:
|
||||
self.logger.info(f"Reading gachas from {file_path}...")
|
||||
|
||||
with open(file_path, encoding="utf-8") as f:
|
||||
reader = csv.DictReader(f)
|
||||
for row in reader:
|
||||
self.ongeki_data.static.put_gacha(
|
||||
row["version"],
|
||||
row["gachaId"],
|
||||
row["gachaName"],
|
||||
row["kind"],
|
||||
type=row["type"],
|
||||
isCeiling=True if row["isCeiling"] == "1" else False,
|
||||
maxSelectPoint=row["maxSelectPoint"],
|
||||
)
|
||||
|
||||
self.logger.info(f"Added gacha {row['gachaId']}")
|
||||
|
||||
def read_ongeki_gacha_card_csv(self, file_path: str) -> None:
|
||||
self.logger.info(f"Reading gacha cards from {file_path}...")
|
||||
|
||||
with open(file_path, encoding="utf-8") as f:
|
||||
reader = csv.DictReader(f)
|
||||
for row in reader:
|
||||
self.ongeki_data.static.put_gacha_card(
|
||||
row["gachaId"],
|
||||
row["cardId"],
|
||||
rarity=row["rarity"],
|
||||
weight=row["weight"],
|
||||
isPickup=True if row["isPickup"] == "1" else False,
|
||||
isSelect=True if row["isSelect"] == "1" else False,
|
||||
)
|
||||
|
||||
self.logger.info(f"Added card {row['cardId']} to gacha")
|
||||
|
||||
def read_ongeki_gacha(self, base_dir: str) -> None:
|
||||
self.logger.info(f"Reading gachas from {base_dir}...")
|
||||
|
||||
# assuming some GachaKinds based on the GachaType
|
||||
type_to_kind = {
|
||||
"Normal": "Normal",
|
||||
"Pickup": "Pickup",
|
||||
"RecoverFiveShotFlag": "BonusRestored",
|
||||
"Free": "Free",
|
||||
"FreeSR": "Free",
|
||||
}
|
||||
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
for dir in dirs:
|
||||
if os.path.exists(f"{root}/{dir}/Gacha.xml"):
|
||||
with open(f"{root}/{dir}/Gacha.xml", "r", encoding="utf-8") as f:
|
||||
troot = ET.fromstring(f.read())
|
||||
|
||||
name = troot.find("Name").find("str").text
|
||||
gacha_id = int(troot.find("Name").find("id").text)
|
||||
|
||||
# skip already existing gachas
|
||||
if (
|
||||
self.ongeki_data.static.get_gacha(
|
||||
OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY, gacha_id
|
||||
)
|
||||
is not None
|
||||
):
|
||||
self.logger.info(
|
||||
f"Gacha {gacha_id} already added, skipping"
|
||||
)
|
||||
continue
|
||||
|
||||
# 1140 is the first bright memory gacha
|
||||
if gacha_id < 1140:
|
||||
version = OngekiConstants.VER_ONGEKI_BRIGHT
|
||||
else:
|
||||
version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
||||
|
||||
gacha_kind = OngekiConstants.CM_GACHA_KINDS[
|
||||
type_to_kind[troot.find("Type").text]
|
||||
].value
|
||||
|
||||
# hardcode which gachas get "Select Gacha" with 33 points
|
||||
is_ceiling, max_select_point = 0, 0
|
||||
if gacha_id in {1163, 1164, 1165, 1166, 1167, 1168}:
|
||||
is_ceiling = 1
|
||||
max_select_point = 33
|
||||
|
||||
self.ongeki_data.static.put_gacha(
|
||||
version,
|
||||
gacha_id,
|
||||
name,
|
||||
gacha_kind,
|
||||
isCeiling=is_ceiling,
|
||||
maxSelectPoint=max_select_point,
|
||||
)
|
||||
self.logger.info(f"Added gacha {gacha_id}")
|
||||
@@ -6,16 +6,5 @@ from titles.cxb.read import CxbReader
|
||||
index = CxbServlet
|
||||
database = CxbData
|
||||
reader = CxbReader
|
||||
|
||||
use_default_title = False
|
||||
include_protocol = True
|
||||
title_secure = True
|
||||
game_codes = [CxbConstants.GAME_CODE]
|
||||
trailing_slash = True
|
||||
use_default_host = False
|
||||
|
||||
include_port = True
|
||||
uri = "http://$h:$p/" # If you care about the allnet response you're probably running with no SSL
|
||||
host = ""
|
||||
|
||||
current_schema_version = 1
|
||||
current_schema_version = 1
|
||||
|
||||
@@ -11,82 +11,91 @@ from titles.cxb.config import CxbConfig
|
||||
from titles.cxb.const import CxbConstants
|
||||
from titles.cxb.database import CxbData
|
||||
|
||||
class CxbBase():
|
||||
|
||||
class CxbBase:
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: CxbConfig) -> None:
|
||||
self.config = cfg # Config file
|
||||
self.config = cfg # Config file
|
||||
self.game_config = game_cfg
|
||||
self.data = CxbData(cfg) # Database
|
||||
self.data = CxbData(cfg) # Database
|
||||
self.game = CxbConstants.GAME_CODE
|
||||
self.logger = logging.getLogger("cxb")
|
||||
self.version = CxbConstants.VER_CROSSBEATS_REV
|
||||
|
||||
|
||||
def handle_action_rpreq_request(self, data: Dict) -> Dict:
|
||||
return({})
|
||||
|
||||
return {}
|
||||
|
||||
def handle_action_hitreq_request(self, data: Dict) -> Dict:
|
||||
return({"data":[]})
|
||||
return {"data": []}
|
||||
|
||||
def handle_auth_usercheck_request(self, data: Dict) -> Dict:
|
||||
profile = self.data.profile.get_profile_index(0, data["usercheck"]["authid"], self.version)
|
||||
profile = self.data.profile.get_profile_index(
|
||||
0, data["usercheck"]["authid"], self.version
|
||||
)
|
||||
if profile is not None:
|
||||
self.logger.info(f"User {data['usercheck']['authid']} has CXB profile")
|
||||
return({"exist": "true", "logout": "true"})
|
||||
return {"exist": "true", "logout": "true"}
|
||||
|
||||
self.logger.info(f"No profile for aime id {data['usercheck']['authid']}")
|
||||
return({"exist": "false", "logout": "true"})
|
||||
return {"exist": "false", "logout": "true"}
|
||||
|
||||
def handle_auth_entry_request(self, data: Dict) -> Dict:
|
||||
self.logger.info(f"New profile for {data['entry']['authid']}")
|
||||
return({"token": data["entry"]["authid"], "uid": data["entry"]["authid"]})
|
||||
return {"token": data["entry"]["authid"], "uid": data["entry"]["authid"]}
|
||||
|
||||
def handle_auth_login_request(self, data: Dict) -> Dict:
|
||||
profile = self.data.profile.get_profile_index(0, data["login"]["authid"], self.version)
|
||||
|
||||
profile = self.data.profile.get_profile_index(
|
||||
0, data["login"]["authid"], self.version
|
||||
)
|
||||
|
||||
if profile is not None:
|
||||
self.logger.info(f"Login user {data['login']['authid']}")
|
||||
return({"token": data["login"]["authid"], "uid": data["login"]["authid"]})
|
||||
|
||||
return {"token": data["login"]["authid"], "uid": data["login"]["authid"]}
|
||||
|
||||
self.logger.warn(f"User {data['login']['authid']} does not have a profile")
|
||||
return({})
|
||||
|
||||
return {}
|
||||
|
||||
def handle_action_loadrange_request(self, data: Dict) -> Dict:
|
||||
range_start = data['loadrange']['range'][0]
|
||||
range_end = data['loadrange']['range'][1]
|
||||
uid = data['loadrange']['uid']
|
||||
range_start = data["loadrange"]["range"][0]
|
||||
range_end = data["loadrange"]["range"][1]
|
||||
uid = data["loadrange"]["uid"]
|
||||
|
||||
self.logger.info(f"Load data for {uid}")
|
||||
profile = self.data.profile.get_profile(uid, self.version)
|
||||
songs = self.data.score.get_best_scores(uid)
|
||||
songs = self.data.score.get_best_scores(uid)
|
||||
|
||||
data1 = []
|
||||
index = []
|
||||
versionindex = []
|
||||
|
||||
|
||||
for profile_index in profile:
|
||||
profile_data = profile_index["data"]
|
||||
|
||||
if int(range_start) == 800000:
|
||||
return({"index":range_start, "data":[], "version":10400})
|
||||
|
||||
if not ( int(range_start) <= int(profile_index[3]) <= int(range_end) ):
|
||||
return {"index": range_start, "data": [], "version": 10400}
|
||||
|
||||
if not (int(range_start) <= int(profile_index[3]) <= int(range_end)):
|
||||
continue
|
||||
#Prevent loading of the coupons within the profile to use the force unlock instead
|
||||
# Prevent loading of the coupons within the profile to use the force unlock instead
|
||||
elif 500 <= int(profile_index[3]) <= 510:
|
||||
continue
|
||||
#Prevent loading of songs saved in the profile
|
||||
# Prevent loading of songs saved in the profile
|
||||
elif 100000 <= int(profile_index[3]) <= 110000:
|
||||
continue
|
||||
#Prevent loading of the shop list / unlocked titles & icons saved in the profile
|
||||
# Prevent loading of the shop list / unlocked titles & icons saved in the profile
|
||||
elif 200000 <= int(profile_index[3]) <= 210000:
|
||||
continue
|
||||
#Prevent loading of stories in the profile
|
||||
# Prevent loading of stories in the profile
|
||||
elif 900000 <= int(profile_index[3]) <= 900200:
|
||||
continue
|
||||
else:
|
||||
index.append(profile_index[3])
|
||||
data1.append(b64encode(bytes(json.dumps(profile_data, separators=(',', ':')), 'utf-8')).decode('utf-8'))
|
||||
data1.append(
|
||||
b64encode(
|
||||
bytes(json.dumps(profile_data, separators=(",", ":")), "utf-8")
|
||||
).decode("utf-8")
|
||||
)
|
||||
|
||||
'''
|
||||
"""
|
||||
100000 = Songs
|
||||
200000 = Shop
|
||||
300000 = Courses
|
||||
@@ -96,101 +105,140 @@ class CxbBase():
|
||||
700000 = rcLog
|
||||
800000 = Partners
|
||||
900000 = Stories
|
||||
'''
|
||||
"""
|
||||
|
||||
# Coupons
|
||||
for i in range(500,510):
|
||||
for i in range(500, 510):
|
||||
index.append(str(i))
|
||||
couponid = int(i) - 500
|
||||
dataValue = [{
|
||||
"couponId":str(couponid),
|
||||
"couponNum":"1",
|
||||
"couponLog":[],
|
||||
}]
|
||||
data1.append(b64encode(bytes(json.dumps(dataValue[0], separators=(',', ':')), 'utf-8')).decode('utf-8'))
|
||||
|
||||
dataValue = [
|
||||
{
|
||||
"couponId": str(couponid),
|
||||
"couponNum": "1",
|
||||
"couponLog": [],
|
||||
}
|
||||
]
|
||||
data1.append(
|
||||
b64encode(
|
||||
bytes(json.dumps(dataValue[0], separators=(",", ":")), "utf-8")
|
||||
).decode("utf-8")
|
||||
)
|
||||
|
||||
# ShopList_Title
|
||||
for i in range(200000,201451):
|
||||
for i in range(200000, 201451):
|
||||
index.append(str(i))
|
||||
shopid = int(i) - 200000
|
||||
dataValue = [{
|
||||
"shopId":shopid,
|
||||
"shopState":"2",
|
||||
"isDisable":"t",
|
||||
"isDeleted":"f",
|
||||
"isSpecialFlag":"f"
|
||||
}]
|
||||
data1.append(b64encode(bytes(json.dumps(dataValue[0], separators=(',', ':')), 'utf-8')).decode('utf-8'))
|
||||
dataValue = [
|
||||
{
|
||||
"shopId": shopid,
|
||||
"shopState": "2",
|
||||
"isDisable": "t",
|
||||
"isDeleted": "f",
|
||||
"isSpecialFlag": "f",
|
||||
}
|
||||
]
|
||||
data1.append(
|
||||
b64encode(
|
||||
bytes(json.dumps(dataValue[0], separators=(",", ":")), "utf-8")
|
||||
).decode("utf-8")
|
||||
)
|
||||
|
||||
#ShopList_Icon
|
||||
for i in range(202000,202264):
|
||||
# ShopList_Icon
|
||||
for i in range(202000, 202264):
|
||||
index.append(str(i))
|
||||
shopid = int(i) - 200000
|
||||
dataValue = [{
|
||||
"shopId":shopid,
|
||||
"shopState":"2",
|
||||
"isDisable":"t",
|
||||
"isDeleted":"f",
|
||||
"isSpecialFlag":"f"
|
||||
}]
|
||||
data1.append(b64encode(bytes(json.dumps(dataValue[0], separators=(',', ':')), 'utf-8')).decode('utf-8'))
|
||||
dataValue = [
|
||||
{
|
||||
"shopId": shopid,
|
||||
"shopState": "2",
|
||||
"isDisable": "t",
|
||||
"isDeleted": "f",
|
||||
"isSpecialFlag": "f",
|
||||
}
|
||||
]
|
||||
data1.append(
|
||||
b64encode(
|
||||
bytes(json.dumps(dataValue[0], separators=(",", ":")), "utf-8")
|
||||
).decode("utf-8")
|
||||
)
|
||||
|
||||
#Stories
|
||||
for i in range(900000,900003):
|
||||
# Stories
|
||||
for i in range(900000, 900003):
|
||||
index.append(str(i))
|
||||
storyid = int(i) - 900000
|
||||
dataValue = [{
|
||||
"storyId":storyid,
|
||||
"unlockState1":["t"] * 10,
|
||||
"unlockState2":["t"] * 10,
|
||||
"unlockState3":["t"] * 10,
|
||||
"unlockState4":["t"] * 10,
|
||||
"unlockState5":["t"] * 10,
|
||||
"unlockState6":["t"] * 10,
|
||||
"unlockState7":["t"] * 10,
|
||||
"unlockState8":["t"] * 10,
|
||||
"unlockState9":["t"] * 10,
|
||||
"unlockState10":["t"] * 10,
|
||||
"unlockState11":["t"] * 10,
|
||||
"unlockState12":["t"] * 10,
|
||||
"unlockState13":["t"] * 10,
|
||||
"unlockState14":["t"] * 10,
|
||||
"unlockState15":["t"] * 10,
|
||||
"unlockState16":["t"] * 10
|
||||
}]
|
||||
data1.append(b64encode(bytes(json.dumps(dataValue[0], separators=(',', ':')), 'utf-8')).decode('utf-8'))
|
||||
dataValue = [
|
||||
{
|
||||
"storyId": storyid,
|
||||
"unlockState1": ["t"] * 10,
|
||||
"unlockState2": ["t"] * 10,
|
||||
"unlockState3": ["t"] * 10,
|
||||
"unlockState4": ["t"] * 10,
|
||||
"unlockState5": ["t"] * 10,
|
||||
"unlockState6": ["t"] * 10,
|
||||
"unlockState7": ["t"] * 10,
|
||||
"unlockState8": ["t"] * 10,
|
||||
"unlockState9": ["t"] * 10,
|
||||
"unlockState10": ["t"] * 10,
|
||||
"unlockState11": ["t"] * 10,
|
||||
"unlockState12": ["t"] * 10,
|
||||
"unlockState13": ["t"] * 10,
|
||||
"unlockState14": ["t"] * 10,
|
||||
"unlockState15": ["t"] * 10,
|
||||
"unlockState16": ["t"] * 10,
|
||||
}
|
||||
]
|
||||
data1.append(
|
||||
b64encode(
|
||||
bytes(json.dumps(dataValue[0], separators=(",", ":")), "utf-8")
|
||||
).decode("utf-8")
|
||||
)
|
||||
|
||||
for song in songs:
|
||||
song_data = song["data"]
|
||||
songCode = []
|
||||
|
||||
songCode.append({
|
||||
"mcode": song_data['mcode'],
|
||||
"musicState": song_data['musicState'],
|
||||
"playCount": song_data['playCount'],
|
||||
"totalScore": song_data['totalScore'],
|
||||
"highScore": song_data['highScore'],
|
||||
"everHighScore": song_data['everHighScore'] if 'everHighScore' in song_data else ["0","0","0","0","0"],
|
||||
"clearRate": song_data['clearRate'],
|
||||
"rankPoint": song_data['rankPoint'],
|
||||
"normalCR": song_data['normalCR'] if 'normalCR' in song_data else ["0","0","0","0","0"],
|
||||
"survivalCR": song_data['survivalCR'] if 'survivalCR' in song_data else ["0","0","0","0","0"],
|
||||
"ultimateCR": song_data['ultimateCR'] if 'ultimateCR' in song_data else ["0","0","0","0","0"],
|
||||
"nohopeCR": song_data['nohopeCR'] if 'nohopeCR' in song_data else ["0","0","0","0","0"],
|
||||
"combo": song_data['combo'],
|
||||
"coupleUserId": song_data['coupleUserId'],
|
||||
"difficulty": song_data['difficulty'],
|
||||
"isFullCombo": song_data['isFullCombo'],
|
||||
"clearGaugeType": song_data['clearGaugeType'],
|
||||
"fieldType": song_data['fieldType'],
|
||||
"gameType": song_data['gameType'],
|
||||
"grade": song_data['grade'],
|
||||
"unlockState": song_data['unlockState'],
|
||||
"extraState": song_data['extraState']
|
||||
})
|
||||
index.append(song_data['index'])
|
||||
data1.append(b64encode(bytes(json.dumps(songCode[0], separators=(',', ':')), 'utf-8')).decode('utf-8'))
|
||||
songCode.append(
|
||||
{
|
||||
"mcode": song_data["mcode"],
|
||||
"musicState": song_data["musicState"],
|
||||
"playCount": song_data["playCount"],
|
||||
"totalScore": song_data["totalScore"],
|
||||
"highScore": song_data["highScore"],
|
||||
"everHighScore": song_data["everHighScore"]
|
||||
if "everHighScore" in song_data
|
||||
else ["0", "0", "0", "0", "0"],
|
||||
"clearRate": song_data["clearRate"],
|
||||
"rankPoint": song_data["rankPoint"],
|
||||
"normalCR": song_data["normalCR"]
|
||||
if "normalCR" in song_data
|
||||
else ["0", "0", "0", "0", "0"],
|
||||
"survivalCR": song_data["survivalCR"]
|
||||
if "survivalCR" in song_data
|
||||
else ["0", "0", "0", "0", "0"],
|
||||
"ultimateCR": song_data["ultimateCR"]
|
||||
if "ultimateCR" in song_data
|
||||
else ["0", "0", "0", "0", "0"],
|
||||
"nohopeCR": song_data["nohopeCR"]
|
||||
if "nohopeCR" in song_data
|
||||
else ["0", "0", "0", "0", "0"],
|
||||
"combo": song_data["combo"],
|
||||
"coupleUserId": song_data["coupleUserId"],
|
||||
"difficulty": song_data["difficulty"],
|
||||
"isFullCombo": song_data["isFullCombo"],
|
||||
"clearGaugeType": song_data["clearGaugeType"],
|
||||
"fieldType": song_data["fieldType"],
|
||||
"gameType": song_data["gameType"],
|
||||
"grade": song_data["grade"],
|
||||
"unlockState": song_data["unlockState"],
|
||||
"extraState": song_data["extraState"],
|
||||
}
|
||||
)
|
||||
index.append(song_data["index"])
|
||||
data1.append(
|
||||
b64encode(
|
||||
bytes(json.dumps(songCode[0], separators=(",", ":")), "utf-8")
|
||||
).decode("utf-8")
|
||||
)
|
||||
|
||||
for v in index:
|
||||
try:
|
||||
@@ -198,66 +246,81 @@ class CxbBase():
|
||||
v_profile_data = v_profile["data"]
|
||||
versionindex.append(int(v_profile_data["appVersion"]))
|
||||
except:
|
||||
versionindex.append('10400')
|
||||
versionindex.append("10400")
|
||||
|
||||
return({"index":index, "data":data1, "version":versionindex})
|
||||
return {"index": index, "data": data1, "version": versionindex}
|
||||
|
||||
def handle_action_saveindex_request(self, data: Dict) -> Dict:
|
||||
save_data = data['saveindex']
|
||||
|
||||
save_data = data["saveindex"]
|
||||
|
||||
try:
|
||||
#REV Omnimix Version Fetcher
|
||||
gameversion = data['saveindex']['data'][0][2]
|
||||
# REV Omnimix Version Fetcher
|
||||
gameversion = data["saveindex"]["data"][0][2]
|
||||
self.logger.warning(f"Game Version is {gameversion}")
|
||||
except:
|
||||
pass
|
||||
|
||||
if "10205" in gameversion:
|
||||
self.logger.info(f"Saving CrossBeats REV profile for {data['saveindex']['uid']}")
|
||||
#Alright.... time to bring the jank code
|
||||
|
||||
for value in data['saveindex']['data']:
|
||||
|
||||
if 'playedUserId' in value[1]:
|
||||
self.data.profile.put_profile(data['saveindex']['uid'], self.version, value[0], value[1])
|
||||
if 'mcode' not in value[1]:
|
||||
self.data.profile.put_profile(data['saveindex']['uid'], self.version, value[0], value[1])
|
||||
if 'shopId' in value:
|
||||
continue
|
||||
if 'mcode' in value[1] and 'musicState' in value[1]:
|
||||
song_json = json.loads(value[1])
|
||||
|
||||
songCode = []
|
||||
songCode.append({
|
||||
"mcode": song_json['mcode'],
|
||||
"musicState": song_json['musicState'],
|
||||
"playCount": song_json['playCount'],
|
||||
"totalScore": song_json['totalScore'],
|
||||
"highScore": song_json['highScore'],
|
||||
"clearRate": song_json['clearRate'],
|
||||
"rankPoint": song_json['rankPoint'],
|
||||
"combo": song_json['combo'],
|
||||
"coupleUserId": song_json['coupleUserId'],
|
||||
"difficulty": song_json['difficulty'],
|
||||
"isFullCombo": song_json['isFullCombo'],
|
||||
"clearGaugeType": song_json['clearGaugeType'],
|
||||
"fieldType": song_json['fieldType'],
|
||||
"gameType": song_json['gameType'],
|
||||
"grade": song_json['grade'],
|
||||
"unlockState": song_json['unlockState'],
|
||||
"extraState": song_json['extraState'],
|
||||
"index": value[0]
|
||||
})
|
||||
self.data.score.put_best_score(data['saveindex']['uid'], song_json['mcode'], self.version, value[0], songCode[0])
|
||||
return({})
|
||||
else:
|
||||
self.logger.info(f"Saving CrossBeats REV Sunrise profile for {data['saveindex']['uid']}")
|
||||
|
||||
#Sunrise
|
||||
if "10205" in gameversion:
|
||||
self.logger.info(
|
||||
f"Saving CrossBeats REV profile for {data['saveindex']['uid']}"
|
||||
)
|
||||
# Alright.... time to bring the jank code
|
||||
|
||||
for value in data["saveindex"]["data"]:
|
||||
if "playedUserId" in value[1]:
|
||||
self.data.profile.put_profile(
|
||||
data["saveindex"]["uid"], self.version, value[0], value[1]
|
||||
)
|
||||
if "mcode" not in value[1]:
|
||||
self.data.profile.put_profile(
|
||||
data["saveindex"]["uid"], self.version, value[0], value[1]
|
||||
)
|
||||
if "shopId" in value:
|
||||
continue
|
||||
if "mcode" in value[1] and "musicState" in value[1]:
|
||||
song_json = json.loads(value[1])
|
||||
|
||||
songCode = []
|
||||
songCode.append(
|
||||
{
|
||||
"mcode": song_json["mcode"],
|
||||
"musicState": song_json["musicState"],
|
||||
"playCount": song_json["playCount"],
|
||||
"totalScore": song_json["totalScore"],
|
||||
"highScore": song_json["highScore"],
|
||||
"clearRate": song_json["clearRate"],
|
||||
"rankPoint": song_json["rankPoint"],
|
||||
"combo": song_json["combo"],
|
||||
"coupleUserId": song_json["coupleUserId"],
|
||||
"difficulty": song_json["difficulty"],
|
||||
"isFullCombo": song_json["isFullCombo"],
|
||||
"clearGaugeType": song_json["clearGaugeType"],
|
||||
"fieldType": song_json["fieldType"],
|
||||
"gameType": song_json["gameType"],
|
||||
"grade": song_json["grade"],
|
||||
"unlockState": song_json["unlockState"],
|
||||
"extraState": song_json["extraState"],
|
||||
"index": value[0],
|
||||
}
|
||||
)
|
||||
self.data.score.put_best_score(
|
||||
data["saveindex"]["uid"],
|
||||
song_json["mcode"],
|
||||
self.version,
|
||||
value[0],
|
||||
songCode[0],
|
||||
)
|
||||
return {}
|
||||
else:
|
||||
self.logger.info(
|
||||
f"Saving CrossBeats REV Sunrise profile for {data['saveindex']['uid']}"
|
||||
)
|
||||
|
||||
# Sunrise
|
||||
try:
|
||||
profileIndex = save_data['index'].index('0')
|
||||
profileIndex = save_data["index"].index("0")
|
||||
except:
|
||||
return({"data":""}) #Maybe
|
||||
return {"data": ""} # Maybe
|
||||
|
||||
profile = json.loads(save_data["data"][profileIndex])
|
||||
aimeId = profile["aimeId"]
|
||||
@@ -265,65 +328,91 @@ class CxbBase():
|
||||
|
||||
for index, value in enumerate(data["saveindex"]["data"]):
|
||||
if int(data["saveindex"]["index"][index]) == 101:
|
||||
self.data.profile.put_profile(aimeId, self.version, data["saveindex"]["index"][index], value)
|
||||
if int(data["saveindex"]["index"][index]) >= 700000 and int(data["saveindex"]["index"][index])<= 701000:
|
||||
self.data.profile.put_profile(aimeId, self.version, data["saveindex"]["index"][index], value)
|
||||
if int(data["saveindex"]["index"][index]) >= 500 and int(data["saveindex"]["index"][index]) <= 510:
|
||||
self.data.profile.put_profile(aimeId, self.version, data["saveindex"]["index"][index], value)
|
||||
if 'playedUserId' in value:
|
||||
self.data.profile.put_profile(aimeId, self.version, data["saveindex"]["index"][index], json.loads(value))
|
||||
if 'mcode' not in value and "normalCR" not in value:
|
||||
self.data.profile.put_profile(aimeId, self.version, data["saveindex"]["index"][index], json.loads(value))
|
||||
if 'shopId' in value:
|
||||
self.data.profile.put_profile(
|
||||
aimeId, self.version, data["saveindex"]["index"][index], value
|
||||
)
|
||||
if (
|
||||
int(data["saveindex"]["index"][index]) >= 700000
|
||||
and int(data["saveindex"]["index"][index]) <= 701000
|
||||
):
|
||||
self.data.profile.put_profile(
|
||||
aimeId, self.version, data["saveindex"]["index"][index], value
|
||||
)
|
||||
if (
|
||||
int(data["saveindex"]["index"][index]) >= 500
|
||||
and int(data["saveindex"]["index"][index]) <= 510
|
||||
):
|
||||
self.data.profile.put_profile(
|
||||
aimeId, self.version, data["saveindex"]["index"][index], value
|
||||
)
|
||||
if "playedUserId" in value:
|
||||
self.data.profile.put_profile(
|
||||
aimeId,
|
||||
self.version,
|
||||
data["saveindex"]["index"][index],
|
||||
json.loads(value),
|
||||
)
|
||||
if "mcode" not in value and "normalCR" not in value:
|
||||
self.data.profile.put_profile(
|
||||
aimeId,
|
||||
self.version,
|
||||
data["saveindex"]["index"][index],
|
||||
json.loads(value),
|
||||
)
|
||||
if "shopId" in value:
|
||||
continue
|
||||
|
||||
# MusicList Index for the profile
|
||||
indexSongList = []
|
||||
for value in data["saveindex"]["index"]:
|
||||
if int(value) in range(100000,110000):
|
||||
if int(value) in range(100000, 110000):
|
||||
indexSongList.append(value)
|
||||
|
||||
|
||||
for index, value in enumerate(data["saveindex"]["data"]):
|
||||
if 'mcode' not in value:
|
||||
if "mcode" not in value:
|
||||
continue
|
||||
if 'playedUserId' in value:
|
||||
if "playedUserId" in value:
|
||||
continue
|
||||
|
||||
|
||||
data1 = json.loads(value)
|
||||
|
||||
songCode = []
|
||||
songCode.append({
|
||||
"mcode": data1['mcode'],
|
||||
"musicState": data1['musicState'],
|
||||
"playCount": data1['playCount'],
|
||||
"totalScore": data1['totalScore'],
|
||||
"highScore": data1['highScore'],
|
||||
"everHighScore": data1['everHighScore'],
|
||||
"clearRate": data1['clearRate'],
|
||||
"rankPoint": data1['rankPoint'],
|
||||
"normalCR": data1['normalCR'],
|
||||
"survivalCR": data1['survivalCR'],
|
||||
"ultimateCR": data1['ultimateCR'],
|
||||
"nohopeCR": data1['nohopeCR'],
|
||||
"combo": data1['combo'],
|
||||
"coupleUserId": data1['coupleUserId'],
|
||||
"difficulty": data1['difficulty'],
|
||||
"isFullCombo": data1['isFullCombo'],
|
||||
"clearGaugeType": data1['clearGaugeType'],
|
||||
"fieldType": data1['fieldType'],
|
||||
"gameType": data1['gameType'],
|
||||
"grade": data1['grade'],
|
||||
"unlockState": data1['unlockState'],
|
||||
"extraState": data1['extraState'],
|
||||
"index": indexSongList[i]
|
||||
})
|
||||
songCode.append(
|
||||
{
|
||||
"mcode": data1["mcode"],
|
||||
"musicState": data1["musicState"],
|
||||
"playCount": data1["playCount"],
|
||||
"totalScore": data1["totalScore"],
|
||||
"highScore": data1["highScore"],
|
||||
"everHighScore": data1["everHighScore"],
|
||||
"clearRate": data1["clearRate"],
|
||||
"rankPoint": data1["rankPoint"],
|
||||
"normalCR": data1["normalCR"],
|
||||
"survivalCR": data1["survivalCR"],
|
||||
"ultimateCR": data1["ultimateCR"],
|
||||
"nohopeCR": data1["nohopeCR"],
|
||||
"combo": data1["combo"],
|
||||
"coupleUserId": data1["coupleUserId"],
|
||||
"difficulty": data1["difficulty"],
|
||||
"isFullCombo": data1["isFullCombo"],
|
||||
"clearGaugeType": data1["clearGaugeType"],
|
||||
"fieldType": data1["fieldType"],
|
||||
"gameType": data1["gameType"],
|
||||
"grade": data1["grade"],
|
||||
"unlockState": data1["unlockState"],
|
||||
"extraState": data1["extraState"],
|
||||
"index": indexSongList[i],
|
||||
}
|
||||
)
|
||||
|
||||
self.data.score.put_best_score(aimeId, data1['mcode'], self.version, indexSongList[i], songCode[0])
|
||||
self.data.score.put_best_score(
|
||||
aimeId, data1["mcode"], self.version, indexSongList[i], songCode[0]
|
||||
)
|
||||
i += 1
|
||||
return({})
|
||||
|
||||
return {}
|
||||
|
||||
def handle_action_sprankreq_request(self, data: Dict) -> Dict:
|
||||
uid = data['sprankreq']['uid']
|
||||
uid = data["sprankreq"]["uid"]
|
||||
self.logger.info(f"Get best rankings for {uid}")
|
||||
p = self.data.score.get_best_rankings(uid)
|
||||
|
||||
@@ -331,90 +420,122 @@ class CxbBase():
|
||||
|
||||
for rank in p:
|
||||
if rank["song_id"] is not None:
|
||||
rankList.append({
|
||||
"sc": [rank["score"],rank["song_id"]],
|
||||
"rid": rank["rev_id"],
|
||||
"clear": rank["clear"]
|
||||
})
|
||||
rankList.append(
|
||||
{
|
||||
"sc": [rank["score"], rank["song_id"]],
|
||||
"rid": rank["rev_id"],
|
||||
"clear": rank["clear"],
|
||||
}
|
||||
)
|
||||
else:
|
||||
rankList.append({
|
||||
"sc": [rank["score"]],
|
||||
"rid": rank["rev_id"],
|
||||
"clear": rank["clear"]
|
||||
})
|
||||
rankList.append(
|
||||
{
|
||||
"sc": [rank["score"]],
|
||||
"rid": rank["rev_id"],
|
||||
"clear": rank["clear"],
|
||||
}
|
||||
)
|
||||
|
||||
return({
|
||||
return {
|
||||
"uid": data["sprankreq"]["uid"],
|
||||
"aid": data["sprankreq"]["aid"],
|
||||
"rank": rankList,
|
||||
"rankx":[1,1,1]
|
||||
})
|
||||
|
||||
"rankx": [1, 1, 1],
|
||||
}
|
||||
|
||||
def handle_action_getadv_request(self, data: Dict) -> Dict:
|
||||
return({"data":[{"r":"1","i":"100300","c":"20"}]})
|
||||
|
||||
return {"data": [{"r": "1", "i": "100300", "c": "20"}]}
|
||||
|
||||
def handle_action_getmsg_request(self, data: Dict) -> Dict:
|
||||
return({"msgs":[]})
|
||||
|
||||
return {"msgs": []}
|
||||
|
||||
def handle_auth_logout_request(self, data: Dict) -> Dict:
|
||||
return({"auth":True})
|
||||
|
||||
def handle_action_rankreg_request(self, data: Dict) -> Dict:
|
||||
uid = data['rankreg']['uid']
|
||||
return {"auth": True}
|
||||
|
||||
def handle_action_rankreg_request(self, data: Dict) -> Dict:
|
||||
uid = data["rankreg"]["uid"]
|
||||
self.logger.info(f"Put {len(data['rankreg']['data'])} rankings for {uid}")
|
||||
|
||||
for rid in data['rankreg']['data']:
|
||||
#REV S2
|
||||
for rid in data["rankreg"]["data"]:
|
||||
# REV S2
|
||||
if "clear" in rid:
|
||||
try:
|
||||
self.data.score.put_ranking(user_id=uid, rev_id=int(rid["rid"]), song_id=int(rid["sc"][1]), score=int(rid["sc"][0]), clear=rid["clear"])
|
||||
self.data.score.put_ranking(
|
||||
user_id=uid,
|
||||
rev_id=int(rid["rid"]),
|
||||
song_id=int(rid["sc"][1]),
|
||||
score=int(rid["sc"][0]),
|
||||
clear=rid["clear"],
|
||||
)
|
||||
except:
|
||||
self.data.score.put_ranking(user_id=uid, rev_id=int(rid["rid"]), song_id=0, score=int(rid["sc"][0]), clear=rid["clear"])
|
||||
#REV
|
||||
self.data.score.put_ranking(
|
||||
user_id=uid,
|
||||
rev_id=int(rid["rid"]),
|
||||
song_id=0,
|
||||
score=int(rid["sc"][0]),
|
||||
clear=rid["clear"],
|
||||
)
|
||||
# REV
|
||||
else:
|
||||
try:
|
||||
self.data.score.put_ranking(user_id=uid, rev_id=int(rid["rid"]), song_id=int(rid["sc"][1]), score=int(rid["sc"][0]), clear=0)
|
||||
self.data.score.put_ranking(
|
||||
user_id=uid,
|
||||
rev_id=int(rid["rid"]),
|
||||
song_id=int(rid["sc"][1]),
|
||||
score=int(rid["sc"][0]),
|
||||
clear=0,
|
||||
)
|
||||
except:
|
||||
self.data.score.put_ranking(user_id=uid, rev_id=int(rid["rid"]), song_id=0, score=int(rid["sc"][0]), clear=0)
|
||||
return({})
|
||||
|
||||
self.data.score.put_ranking(
|
||||
user_id=uid,
|
||||
rev_id=int(rid["rid"]),
|
||||
song_id=0,
|
||||
score=int(rid["sc"][0]),
|
||||
clear=0,
|
||||
)
|
||||
return {}
|
||||
|
||||
def handle_action_addenergy_request(self, data: Dict) -> Dict:
|
||||
uid = data['addenergy']['uid']
|
||||
uid = data["addenergy"]["uid"]
|
||||
self.logger.info(f"Add energy to user {uid}")
|
||||
profile = self.data.profile.get_profile_index(0, uid, self.version)
|
||||
data1 = profile["data"]
|
||||
p = self.data.item.get_energy(uid)
|
||||
energy = p["energy"]
|
||||
|
||||
if not p:
|
||||
|
||||
if not p:
|
||||
self.data.item.put_energy(uid, 5)
|
||||
|
||||
return({
|
||||
|
||||
return {
|
||||
"class": data1["myClass"],
|
||||
"granted": "5",
|
||||
"total": "5",
|
||||
"threshold": "1000"
|
||||
})
|
||||
"threshold": "1000",
|
||||
}
|
||||
|
||||
array = []
|
||||
|
||||
|
||||
newenergy = int(energy) + 5
|
||||
self.data.item.put_energy(uid, newenergy)
|
||||
|
||||
if int(energy) <= 995:
|
||||
array.append({
|
||||
"class": data1["myClass"],
|
||||
"granted": "5",
|
||||
"total": str(energy),
|
||||
"threshold": "1000"
|
||||
})
|
||||
array.append(
|
||||
{
|
||||
"class": data1["myClass"],
|
||||
"granted": "5",
|
||||
"total": str(energy),
|
||||
"threshold": "1000",
|
||||
}
|
||||
)
|
||||
else:
|
||||
array.append({
|
||||
"class": data1["myClass"],
|
||||
"granted": "0",
|
||||
"total": str(energy),
|
||||
"threshold": "1000"
|
||||
})
|
||||
array.append(
|
||||
{
|
||||
"class": data1["myClass"],
|
||||
"granted": "0",
|
||||
"total": str(energy),
|
||||
"threshold": "1000",
|
||||
}
|
||||
)
|
||||
return array[0]
|
||||
|
||||
def handle_action_eventreq_request(self, data: Dict) -> Dict:
|
||||
|
||||
@@ -1,40 +1,60 @@
|
||||
from core.config import CoreConfig
|
||||
|
||||
class CxbServerConfig():
|
||||
|
||||
class CxbServerConfig:
|
||||
def __init__(self, parent_config: "CxbConfig"):
|
||||
self.__config = parent_config
|
||||
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'enable', default=True)
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "cxb", "server", "enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def loglevel(self) -> int:
|
||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'loglevel', default="info"))
|
||||
return CoreConfig.str_to_loglevel(
|
||||
CoreConfig.get_config_field(
|
||||
self.__config, "cxb", "server", "loglevel", default="info"
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def hostname(self) -> str:
|
||||
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'hostname', default="localhost")
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "cxb", "server", "hostname", default="localhost"
|
||||
)
|
||||
|
||||
@property
|
||||
def ssl_enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'ssl_enable', default=False)
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "cxb", "server", "ssl_enable", default=False
|
||||
)
|
||||
|
||||
@property
|
||||
def port(self) -> int:
|
||||
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'port', default=8082)
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "cxb", "server", "port", default=8082
|
||||
)
|
||||
|
||||
@property
|
||||
def port_secure(self) -> int:
|
||||
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'port_secure', default=443)
|
||||
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "cxb", "server", "port_secure", default=443
|
||||
)
|
||||
|
||||
@property
|
||||
def ssl_cert(self) -> str:
|
||||
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'ssl_cert', default="cert/title.crt")
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "cxb", "server", "ssl_cert", default="cert/title.crt"
|
||||
)
|
||||
|
||||
@property
|
||||
def ssl_key(self) -> str:
|
||||
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'ssl_key', default="cert/title.key")
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "cxb", "server", "ssl_key", default="cert/title.key"
|
||||
)
|
||||
|
||||
|
||||
class CxbConfig(dict):
|
||||
def __init__(self) -> None:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class CxbConstants():
|
||||
class CxbConstants:
|
||||
GAME_CODE = "SDCA"
|
||||
|
||||
CONFIG_NAME = "cxb.yaml"
|
||||
@@ -8,8 +8,13 @@ class CxbConstants():
|
||||
VER_CROSSBEATS_REV_SUNRISE_S2 = 2
|
||||
VER_CROSSBEATS_REV_SUNRISE_S2_OMNI = 3
|
||||
|
||||
VERSION_NAMES = ("crossbeats REV.", "crossbeats REV. SUNRISE", "crossbeats REV. SUNRISE S2", "crossbeats REV. SUNRISE S2 Omnimix")
|
||||
VERSION_NAMES = (
|
||||
"crossbeats REV.",
|
||||
"crossbeats REV. SUNRISE",
|
||||
"crossbeats REV. SUNRISE S2",
|
||||
"crossbeats REV. SUNRISE S2 Omnimix",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def game_ver_to_string(cls, ver: int):
|
||||
return cls.VERSION_NAMES[ver]
|
||||
return cls.VERSION_NAMES[ver]
|
||||
|
||||
474
titles/cxb/data/Export.csv
Normal file
474
titles/cxb/data/Export.csv
Normal file
@@ -0,0 +1,474 @@
|
||||
index,mcode,name,artist,category,easy,standard,hard,master,unlimited,
|
||||
100000,tutori2,Tutorial,Tutorial,Unknown,Easy N/A,Standard N/A,Hard N/A,Master N/A,Unlimited N/A,
|
||||
100000,tutori3,Tutorial,Tutorial,Unknown,Easy N/A,Standard N/A,Hard N/A,Master N/A,Unlimited N/A,
|
||||
100000,tutori4,Tutorial,Tutorial,Pick-Up J-Pop (New),Easy N/A,Standard N/A,Hard N/A,Master N/A,Unlimited N/A,
|
||||
100000,tutori6,Tutorial,Tutorial,Pick-Up J-Pop (New),Easy N/A,Standard N/A,Hard N/A,Master N/A,Unlimited N/A,
|
||||
100000,tutori8,白鳥の湖 (Short Remix),,Original,Easy N/A,Standard 3,Hard 15,Master 35,Unlimited N/A,
|
||||
100300,sateli,Satellite System ft.Diana Chiaki,GRATEC MOUR,Original,Easy 17,Standard 28,Hard 49,Master 77,Unlimited 82,
|
||||
100301,nature,Human Nature,Z pinkpong,Original,Easy 5,Standard 14,Hard 24,Master 53,Unlimited 75,
|
||||
100307,purple,DEEP PURPLE,NAOKI,Original,Easy 14,Standard 22,Hard 54,Master 64,Unlimited 73,
|
||||
100308,hearts,Heartstrings,Nhato,Original,Easy 8,Standard 18,Hard 38,Master 68,Unlimited 77,
|
||||
100310,phasea,Phase Angel,OCOT,Original,Easy 9,Standard 16,Hard 38,Master 65,Unlimited 75,
|
||||
100311,planet,Planet Calling,Nyolfen,Original,Easy 10,Standard 17,Hard 36,Master 49,Unlimited 71,
|
||||
100314,firefo,Firefox,Go-qualia,Original,Easy 7,Standard 13,Hard 36,Master 57,Unlimited 83,
|
||||
100315,kounen,光年(konen),小野秀幸,Original,Easy 10,Standard 21,Hard 40,Master 66,Unlimited 78,
|
||||
100316,essenc,Another Essence,RAM,Original,Easy 11,Standard 25,Hard 50,Master 70,Unlimited 76,
|
||||
100317,summer,Summer End Anthem,Personative,Original,Easy 13,Standard 23,Hard 57,Master 79,Unlimited 89,
|
||||
100319,tanosi,たのしいことだけ,Yamajet,Original,Easy 16,Standard 25,Hard 45,Master 70,Unlimited 80,
|
||||
100320,picora,ピコラセテ,TORIENA,Original,Easy 8,Standard 15,Hard 38,Master 66,Unlimited 75,
|
||||
100323,devils,Devil's Classic,Tatsh,Original,Easy 15,Standard 27,Hard 40,Master 80,Unlimited N/A,
|
||||
100328,techno,Techno Highway,SIMON,Original,Easy 9,Standard 16,Hard 38,Master 51,Unlimited 74,
|
||||
100335,glowww,GLOW,Shoichiro Hirata feat. Ellie,Original,Easy 8,Standard 17,Hard 28,Master 42,Unlimited 60,
|
||||
100336,powerr,Power,Dubscribe,Original,Easy 12,Standard 19,Hard 38,Master 69,Unlimited 79,
|
||||
100340,amater,Amateras,Sakuzyo,Original,Easy 13,Standard 21,Hard 48,Master 65,Unlimited 79,
|
||||
100349,advers,Adverse Effect,Rin,Original,Easy 9,Standard 15,Hard 48,Master 71,Unlimited 83,
|
||||
100353,venera,Venerated,Tosh,Original,Easy 8,Standard 15,Hard 43,Master 68,Unlimited 75,
|
||||
100357,dazaii,堕罪,HAKKYOU-KUN feat.せつな,Original,Easy 12,Standard 21,Hard 43,Master 73,Unlimited 77,
|
||||
100365,thesig,The Signs Of The Last Day,SLAKE,Original,Easy 10,Standard 21,Hard 38,Master 56,Unlimited 73,
|
||||
100344,hosita,星達のメロディ,ゆいこんぬ,Original,Easy 10,Standard 16,Hard 36,Master 48,Unlimited 65,
|
||||
100372,bluede,Blue Destiny Blue,NAOKI feat. Florence McNair,Original,Easy 12,Standard 22,Hard 41,Master 58,Unlimited 70,
|
||||
100373,emerao,EMERALD♡KISS ~Original Side~,jun with Aimee,Original,Easy 19,Standard 30,Hard 53,Master 85,Unlimited N/A,
|
||||
100129,megaro,MEGALOMAN[i]A,TITANZ,Original,Easy 0,Standard 55,Hard 80,Master 93,Unlimited 98,
|
||||
100330,angeli,angelik-vice,void,Original,Easy 22,Standard 33,Hard 56,Master 82,Unlimited 90,
|
||||
100342,moonli,月鳴 -moonlit urge-,AZURE FACTORY,Original,Easy 8,Standard 14,Hard 43,Master 61,Unlimited 73,
|
||||
100369,yumemi,ユメミル船,yozuca*,Original,Easy 6,Standard 12,Hard 35,Master 59,Unlimited 69,
|
||||
100348,pinkym,Pinky Magic,Junk,Original,Easy 16,Standard 24,Hard 44,Master 74,Unlimited 81,
|
||||
100370,dynami2,DYNAMITE SENSATION REV.,NAOKI feat. Hyphen,Original,Easy 8,Standard 18,Hard 51,Master 78,Unlimited 80,
|
||||
100306,reseed3,Reseed (Another Edit),quick master,Original,Easy 10,Standard 20,Hard 55,Master 76,Unlimited 80,
|
||||
100002,toucho,Touch Of Gold,Togo Project feat. Frances Maya,Original,Easy 5,Standard 9,Hard 28,Master 44,Unlimited 65,
|
||||
100003,ameoto,雨の音が虹を呼ぶ,Barbarian On The Groove feat.霜月はるか,Original,Easy 6,Standard 12,Hard 26,Master 47,Unlimited 63,
|
||||
100004,kimito,キミとMUSIC,CooRie,Original,Easy 7,Standard 10,Hard 26,Master 49,Unlimited 66,
|
||||
100021,giantk,Giant Killing,R-Lab,Original,Easy 11,Standard 25,Hard 53,Master 71,Unlimited 78,
|
||||
100015,breakd,Break down,GARNiDELiA,Original,Easy 11,Standard 23,Hard 34,Master 57,Unlimited 74,
|
||||
100028,dazzlj,DAZZLING♡SEASON (Japanese Side),jun,Original,Easy 16,Standard 35,Hard 60,Master 80,Unlimited 90,
|
||||
100093,ididid,I.D.,Tatsh feat. 彩音,Original,Easy 16,Standard 29,Hard 46,Master 72,Unlimited 81,
|
||||
100042,sundro,Sundrop,Yamajet,Original,Easy 14,Standard 24,Hard 47,Master 75,Unlimited 83,
|
||||
100063,auflcb,some day (instrumental),NAOKI,Original,Easy 8,Standard 13,Hard 43,Master 81,Unlimited N/A,
|
||||
100045,dennou,電脳少女は歌姫の夢を見るか?,デスおはぎ feat.蛮,Original,Easy 15,Standard 29,Hard 60,Master 76,Unlimited 87,
|
||||
100068,hokoro,ホコロビシロガールズ,むかしばなし,Original,Easy 14,Standard 29,Hard 57,Master 71,Unlimited 81,
|
||||
100005,landin,Landing on the moon,SIMON,Original,Easy 13,Standard 26,Hard 33,Master 49,Unlimited 67,
|
||||
100362,tomorr,Tomorrow,桜井零士,Original,Easy 8,Standard 15,Hard 24,Master 44,Unlimited 62,
|
||||
100363,daybyd,day by day,海辺,Original,Easy 6,Standard 13,Hard 26,Master 38,Unlimited 59,
|
||||
100309,syoujo,生々世々,SADA,Original,Easy 8,Standard 19,Hard 35,Master 53,Unlimited 78,
|
||||
100352,destru,Destrudo,D-Fener,Original,Easy 10,Standard 19,Hard 41,Master 62,Unlimited 72,
|
||||
100041,gingat,Re:Milky way,イトヲカシ,Original,Easy 5,Standard 13,Hard 29,Master 46,Unlimited 61,
|
||||
100066,daisak,大殺界がらくたシンパシー,まふまふ,Original,Easy 12,Standard 28,Hard 36,Master 60,Unlimited 75,
|
||||
100376,paradi,Paradise Regained,LC:AZE feat.chakk,Original,Easy 10,Standard 16,Hard 28,Master 53,Unlimited 64,
|
||||
100377,pigooo,PIG-O,NNNNNNNNNN,Original,Easy 13,Standard 19,Hard 34,Master 59,Unlimited 84,
|
||||
100386,season,The Four Seasons -SPRING- (Remix Ver.),,Variety,Easy 8,Standard 15,Hard 28,Master 44,Unlimited 65,
|
||||
100387,canonn,カノン (Remix Ver.),,Variety,Easy 7,Standard 17,Hard 28,Master 50,Unlimited 83,
|
||||
100388,rhapso,Rhapsody in Blue (Remix Ver.),,Variety,Easy 6,Standard 18,Hard 34,Master 62,Unlimited 74,
|
||||
100389,turkis,トルコ行進曲 (Remix Ver.),,Variety,Easy 11,Standard 19,Hard 39,Master 64,Unlimited 84,
|
||||
100390,biohaz,code_,umbrella Cores,Variety,Easy 6,Standard 15,Hard 30,Master 51,Unlimited 64,
|
||||
100391,monhan,英雄の証 ~ 4Version,カプコンサウンドチーム,Variety,Easy 5,Standard 10,Hard 26,Master 36,Unlimited 54,
|
||||
100392,gyakut2,追求 ~最終プロモーションバージョン (crossbeats REV.アレンジ),岩垂 徳行,Variety,Easy 5,Standard 13,Hard 35,Master 43,Unlimited 56,
|
||||
100393,street,Theme of Ryu -SFIV Arrange-,Capcom Sound Team / Hideyuki Fukasawa,Variety,Easy 7,Standard 13,Hard 34,Master 47,Unlimited 66,
|
||||
100394,rockma2,Dr. WILY STAGE 1 -OMEGAMAN MIX-,ROCK-MEN,Variety,Easy 14,Standard 21,Hard 34,Master 49,Unlimited 76,
|
||||
100374,auflcb3,SOMEDAY -00.prologue-,TЁЯRA,Original,Easy 6,Standard 16,Hard 36,Master 66,Unlimited 86,
|
||||
100325,irohaa,Iroha,Ryunosuke Kudo,Original,Easy 12,Standard 19,Hard 41,Master 55,Unlimited 76,
|
||||
100326,ibelie,I Believe Someday,SPARKER,Original,Easy 14,Standard 27,Hard 47,Master 78,Unlimited 82,
|
||||
100409,monhan2,灼熱の刃 ~ ディノバルド,カプコンサウンドチーム,Variety,Easy 6,Standard 12,Hard 24,Master 43,Unlimited 68,
|
||||
100410,monhan3,古代の息吹き,カプコンサウンドチーム,Variety,Easy 8,Standard 18,Hard 28,Master 45,Unlimited 73,
|
||||
100418,yejiii,YEJI,ginkiha,Original,Easy 10,Standard 22,Hard 36,Master 63,Unlimited 79,
|
||||
100419,histor,HISTORIA,Cranky,Original,Easy 11,Standard 20,Hard 36,Master 56,Unlimited 82,
|
||||
100338,chaset,Chase the WAVE,Tatsh feat. AKINO with bless4,Original,Easy 8,Standard 15,Hard 31,Master 58,Unlimited 76,
|
||||
100412,metall,Metallical parade,Vice Principal,Original,Easy 8,Standard 16,Hard 28,Master 57,Unlimited 77,
|
||||
100327,letmeg,Let Me Give You My Heart,brinq,Original,Easy 12,Standard 18,Hard 32,Master 50,Unlimited 72,
|
||||
100010,hontno,ホントのワタシ,mao,Original,Easy 9,Standard 12,Hard 26,Master 53,Unlimited 66,
|
||||
100024,azitat,Azitate,void,Original,Easy 14,Standard 24,Hard 55,Master 70,Unlimited 83,
|
||||
100360,hellom,Hello Mr.crosbie,民安★ROCK,Original,Easy 7,Standard 18,Hard 37,Master 58,Unlimited 72,
|
||||
100337,laught,Perfect laughter,ぽんず loved by yksb,Original,Easy 7,Standard 20,Hard 35,Master 51,Unlimited 71,
|
||||
100426,bluede2,Blue Destiny Blue ETERNAL,NAOKI feat. Florence McNair,Original,Easy 9,Standard 16,Hard 36,Master 56,Unlimited 81,
|
||||
100423,street2,Ultra Street Fighter IV,Hideyuki Fukasawa,Variety,Easy 14,Standard 21,Hard 37,Master 55,Unlimited 73,
|
||||
100424,street3,Theme of Chun-Li -SFIV Arrange-,Capcom Sound Team / Hideyuki Fukasawa,Variety,Easy 13,Standard 24,Hard 38,Master 57,Unlimited 74,
|
||||
100425,street4,Street Fighter V,Masahiro Aoki,Variety,Easy 11,Standard 17,Hard 32,Master 51,Unlimited 78,
|
||||
100421,silbur,Silbury Sign,カヒーナムジカ,Original,Easy 9,Standard 20,Hard 35,Master 54,Unlimited 75,
|
||||
100422,spicaa,Spica,Endorfin.,Original,Easy 10,Standard 23,Hard 36,Master 56,Unlimited 78,
|
||||
100438,tricko,Trick Or Treat,SLAKE,Original,Easy 11,Standard 23,Hard 34,Master 56,Unlimited 75,
|
||||
100435,thisis,THIS IS HDM,Relect,Original,Easy 12,Standard 23,Hard 37,Master 60,Unlimited 74,
|
||||
100436,rising,Rising Day ft. Satan,GRATEC MOUR,Original,Easy 14,Standard 23,Hard 38,Master 66,Unlimited 85,
|
||||
100411,orbita,Orbital velocity,Vice Principal,Original,Easy 12,Standard 20,Hard 38,Master 62,Unlimited 74,
|
||||
100433,dddddd,D,六弦アリス,Original,Easy 9,Standard 13,Hard 33,Master 53,Unlimited 69,
|
||||
100427,pyroma,Pyromania,KO3,Original,Easy 8,Standard 22,Hard 42,Master 70,Unlimited 84,
|
||||
100312,touchn,Touch n Go,Paisley Parks,Original,Easy 15,Standard 27,Hard 46,Master 68,Unlimited 86,
|
||||
100359,onlyll,only L,emon,Original,Easy 13,Standard 21,Hard 32,Master 56,Unlimited 69,
|
||||
100313,upside,Upside Down,Nave ft.Mayu Wakisaka,Original,Easy 8,Standard 18,Hard 27,Master 48,Unlimited 67,
|
||||
100322,istanb,İstanbul,REVen-G,Original,Easy 23,Standard 41,Hard 49,Master 90,Unlimited 98,
|
||||
100371,memori,Memoria ~終焉を司る荊姫の静粛なる宴~,Astilbe × arendsii,Original,Easy 13,Standard 26,Hard 38,Master 65,Unlimited 84,
|
||||
100350,straye,Strayer,Taishi,Original,Easy 14,Standard 25,Hard 36,Master 61,Unlimited 73,
|
||||
100358,rearhy,Rearhythm,CooRie,Original,Easy 7,Standard 17,Hard 32,Master 52,Unlimited 69,
|
||||
100432,hereco,Here comes the sun ~For you~,Z pinkpong,Original,Easy 11,Standard 16,Hard 34,Master 51,Unlimited 68,
|
||||
100441,thesun,THE SUN,Tatsh,Original,Easy 13,Standard 25,Hard 40,Master 72,Unlimited 87,
|
||||
100343,sayona,さよなら最終列車,むかしばなし,Original,Easy 10,Standard 20,Hard 34,Master 53,Unlimited 71,
|
||||
100380,flameu,Flame Up,Inu Machine,Original,Easy 10,Standard 18,Hard 36,Master 57,Unlimited 65,
|
||||
100434,raidon,RAiD on Mars,sky_delta,Original,Easy 13,Standard 30,Hard 38,Master 58,Unlimited 87,
|
||||
100437,riseup,Rise Up,Dubscribe,Original,Easy 7,Standard 18,Hard 41,Master 67,Unlimited 77,
|
||||
100431,sunglo,Sunglow,Yamajet feat. ひうらまさこ,Original,Easy 10,Standard 18,Hard 39,Master 59,Unlimited 72,
|
||||
100439,kinbos,金星(kinboshi),Hideyuki Ono,Original,Easy 12,Standard 22,Hard 38,Master 64,Unlimited 77,
|
||||
100430,densho,電脳少女と機械仕掛けの神,Chimera music.,Original,Easy 17,Standard 28,Hard 42,Master 74,Unlimited 90,
|
||||
100471,aiohoo,愛をほおばりたいッ!~Like a Monkey!~,新堂敦士,J-Pop,Easy 10,Standard 18,Hard 29,Master 42,Unlimited 66,
|
||||
100472,entert,エンターテイナー (Remix ver.),,Variety,Easy 9,Standard 15,Hard 33,Master 54,Unlimited 87,
|
||||
100457,takeit,Take It Back,Daniel Seven,Original,Easy 12,Standard 20,Hard 38,Master 65,Unlimited 83,
|
||||
100449,harmon,Harmony,ピクセルビー,Original,Easy 12,Standard 20,Hard 36,Master 49,Unlimited 65,
|
||||
100428,avemar,アヴェ・マリア (Remix ver.),,Variety,Easy 8,Standard 16,Hard 31,Master 53,Unlimited 75,
|
||||
100429,mateki,復讐の炎は地獄のように我が心に燃え (Remix ver.),,Variety,Easy 10,Standard 18,Hard 35,Master 54,Unlimited 79,
|
||||
100445,lovech,LOVE CHASE,大島はるな,Original,Easy 8,Standard 16,Hard 30,Master 46,Unlimited 68,
|
||||
100473,akaihe,赤いヘッドホン,新堂敦士,J-Pop,Easy 8,Standard 21,Hard 32,Master 50,Unlimited 69,
|
||||
100474,juicys,Juicy! ~幸せスパイラル~,新堂敦士,J-Pop,Easy 10,Standard 18,Hard 26,Master 45,Unlimited 83,
|
||||
100468,codena,CODENAMEはEQ,TORIENA,Original,Easy 9,Standard 18,Hard 35,Master 48,Unlimited 68,
|
||||
100475,groove,LINK LINK FEVER!!!(グルーヴコースター 3 リンクフィーバーより),リンカ (CV:豊田萌絵),Variety,Easy 10,Standard 22,Hard 40,Master 52,Unlimited 73,
|
||||
100450,kansho,観賞用マーメイド,ヤマイ,Original,Easy 7,Standard 13,Hard 27,Master 55,Unlimited 74,
|
||||
100486,overcl2,Over Clock ~前兆~,NAOKI feat. un∞limited,Original,Easy 12,Standard 23,Hard 42,Master 58,Unlimited 74,
|
||||
100483,taikoo,SAKURA EXHAUST,RIO HAMAMOTO(BNSI)「太鼓の達人」より,Variety,Easy 6,Standard 13,Hard 39,Master 50,Unlimited 75,
|
||||
100480,groove2,QLWA(グルーヴコースター 3 リンクフィーバーより),t+pazolite,Variety,Easy 9,Standard 15,Hard 40,Master 58,Unlimited 85,
|
||||
100487,overcl,Over Clock ~開放~,NAOKI feat. un∞limited,Original,Easy 8,Standard 12,Hard 35,Master 57,Unlimited 86,
|
||||
100466,notoss,Notos,ginkiha,Original,Easy 8,Standard 12,Hard 42,Master 65,Unlimited 91,
|
||||
100447,machup,マチュ☆ピチュ,コツキミヤ,Original,Easy 9,Standard 17,Hard 32,Master 41,Unlimited 71,
|
||||
100488,groove3,カリソメ(グルーヴコースター 3 リンクフィーバーより),コンプ(豚乙女) × ichigo(岸田教団 & THE明星ロケッツ),Touhou + Variety,Easy 10,Standard 18,Hard 34,Master 64,Unlimited 85,
|
||||
100489,groove4,そして誰もいなくなった(グルーヴコースター 3 リンクフィーバーより),コバヤシユウヤ(IOSYS) × あにー(TaNaBaTa),Touhou + Variety,Easy 12,Standard 22,Hard 35,Master 50,Unlimited 75,
|
||||
100482,everyt,EVERYTHING,Tatsh feat.小田ユウ,Original,Easy 13,Standard 22,Hard 30,Master 74,Unlimited N/A,
|
||||
100465,lespri,L'esprit,Cosine,Original,Easy 13,Standard 25,Hard 57,Master 80,Unlimited N/A,
|
||||
100491,groove5,グルーヴ・ザ・ハート(グルーヴコースター 3 リンクフィーバーより),ビートまりお+あまね,Variety,Easy 14,Standard 24,Hard 37,Master 67,Unlimited N/A,
|
||||
100490,honeyo,HONEY♡SUNRiSE ~Original Side~,jun with Aimee,Original,Easy 24,Standard 32,Hard 63,Master 88,Unlimited 93,
|
||||
100494,groove6,Got hive of Ra(グルーヴコースター 3 リンクフィーバーより),E.G.G.,Variety,Easy 22,Standard 30,Hard 64,Master 79,Unlimited N/A,
|
||||
100495,sunglo2,Sunglow (Happy Hardcore Style),Yamajet feat. ひうらまさこ,Original,Easy 11,Standard 21,Hard 36,Master 67,Unlimited 81,
|
||||
100498,fourte,14th Clock,INNOCENT NOIZE,Original,Easy 14,Standard 24,Hard 50,Master 74,Unlimited 80,
|
||||
100496,monhan4,英雄の証/MHF-G 2015 Version,若林タカツグ,Variety,Easy 5,Standard 12,Hard 40,Master 51,Unlimited 62,
|
||||
100497,monhan5,異ヲ辿リシモノ -対峙-,若林タカツグ,Variety,Easy 10,Standard 12,Hard 35,Master 42,Unlimited 65,
|
||||
100504,darkpa,Dark Parashu,INNOCENT NOIZE,Original,Easy 16,Standard 26,Hard 39,Master 70,Unlimited 84,
|
||||
100505,hervor,Hervor,INNOCENT NOIZE,Original,Easy 18,Standard 28,Hard 39,Master 73,Unlimited 81,
|
||||
100499,cirnon,チルノのパーフェクトさんすう教室,ARM+夕野ヨシミ (IOSYS) feat. miko,Touhou,Easy 17,Standard 24,Hard 40,Master 60,Unlimited 79,
|
||||
100500,marisa,魔理沙は大変なものを盗んでいきました,ARM+夕野ヨシミ (IOSYS) feat. 藤咲かりん,Touhou,Easy 18,Standard 25,Hard 41,Master 62,Unlimited 85,
|
||||
100501,yakini,究極焼肉レストラン!お燐の地獄亭!,ARM+夕野ヨシミ (IOSYS) feat. 藤枝あかね,Touhou,Easy 13,Standard 25,Hard 34,Master 58,Unlimited 82,
|
||||
100502,justic,ジャスティス・オブ・ザ・界隈 ~ALL IS FAIR IN LOVE AND ALIMARI~,void (IOSYS) feat.山本椛,Touhou,Easy 14,Standard 19,Hard 36,Master 57,Unlimited 83,
|
||||
100503,sintyo,進捗どうですか?,sumijun feat.ななひら,Touhou,Easy 16,Standard 25,Hard 46,Master 70,Unlimited 83,
|
||||
100347,ascand,Ascendanz,void,Original,Easy 18,Standard 32,Hard 54,Master 80,Unlimited 90,
|
||||
100506,blackl,Black Lotus,Maozon,Original,Easy 12,Standard 19,Hard 41,Master 73,Unlimited 84,
|
||||
100043,childr,チルドレン・オートマトン~ある歌声の亡霊~,あさまっく,Original,Easy 14,Standard 24,Hard 39,Master 56,Unlimited 62,
|
||||
100044,tsukai,ツカイステ・デッドワールド,コゲ犬×ゆちゃ,Original,Easy 13,Standard 19,Hard 44,Master 72,Unlimited 76,
|
||||
100067,rideon,RIDE ON NOW!,さつき が てんこもり feat.un:c,Original,Easy 16,Standard 29,Hard 41,Master 60,Unlimited 80,
|
||||
100507,minest,Minestrone,orangentle,Original,Easy 13,Standard 21,Hard 39,Master 62,Unlimited 76,
|
||||
100508,ordine,Ordine,orangentle,Original,Easy 19,Standard 25,Hard 43,Master 73,Unlimited 82,
|
||||
100509,dreamw,DReamWorKer,LC:AZE,Original,Easy 16,Standard 26,Hard 37,Master 62,Unlimited 75,
|
||||
100510,minerv,Minerva,xi,Original,Easy 25,Standard 32,Hard 61,Master 90,Unlimited N/A,
|
||||
100001,wannab,Wanna Be Your Special,Shoichiro Hirata feat. SUIMI,Original,Easy 5,Standard 9,Hard 23,Master 40,Unlimited 65,
|
||||
100511,sekain,世界の果て,Yamajet,Original,Easy 16,Standard 26,Hard 39,Master 69,Unlimited 78,
|
||||
100512,farawa,Faraway,ミフメイ,Original,Easy 18,Standard 23,Hard 36,Master 60,Unlimited 76,
|
||||
100100,crissc,Crisscrosser,void,Original,Easy 17,Standard 37,Hard 63,Master 86,Unlimited 91,
|
||||
100324,speedy,Awake Speedy,DJ MURASAME,Original,Easy 11,Standard 22,Hard 55,Master 77,Unlimited N/A,
|
||||
100513,xxxrev,XXX-revolt,void feat. KOTOKO,Original,Easy 15,Standard 21,Hard 34,Master 56,Unlimited 73,
|
||||
100016,higame,Hi,Go-qualia,Original,Easy 13,Standard 20,Hard 30,Master 58,Unlimited 71,
|
||||
100022,theepi,The Epic,Cranky,Original,Easy 14,Standard 19,Hard 40,Master 61,Unlimited 75,
|
||||
100023,anomie,Anomie,D-Fener,Original,Easy 15,Standard 22,Hard 38,Master 61,Unlimited 77,
|
||||
100524,crocus,Crocus,村瀬悠太,Original,Easy 15,Standard 26,Hard 37,Master 60,Unlimited 72,
|
||||
100546,lavien,La vie en Fleurs,VILA,Original,Easy 18,Standard 27,Hard 41,Master 71,Unlimited 80,
|
||||
100361,megaro2,MEGALOMAN[i]A -2nd IMPACT-,NEO-G,Original,Easy N/A,Standard N/A,Hard N/A,Master 99,Unlimited 100,
|
||||
100541,chipnn,Chip Notch Educ@tion,yaseta feat. chip_Notch,Original,Easy 16,Standard 27,Hard 34,Master 61,Unlimited 79,
|
||||
100007,yiyoyi,Wanyo Wanyo,MC Natsack,Original,Easy 7,Standard 14,Hard 33,Master 56,Unlimited 70,
|
||||
100014,binary,Binary Overdrive,フラット3rd,Original,Easy 14,Standard 17,Hard 35,Master 64,Unlimited 89,
|
||||
100054,makaim,魔界村 (平地BGM),Remixed by ARM (IOSYS),Original + Variety,Easy 23,Standard 30,Hard 50,Master 77,Unlimited N/A,
|
||||
100055,gyakut,逆転裁判 (綾里真宵 ~逆転姉妹のテーマ),Remixed by OSTER project,Original + Variety,Easy 6,Standard 15,Hard 21,Master 46,Unlimited 64,
|
||||
100056,basara,戦国BASARA (SENGOKU BASARA),Remixed by SOUND HOLIC,Original + Variety,Easy 14,Standard 19,Hard 37,Master 64,Unlimited 73,
|
||||
100514,daybre,DAYBREAK FRONTLINE,Orangestar,Vocaloid,Easy 9,Standard 16,Hard 32,Master 53,Unlimited 72,
|
||||
100515,umiyur,ウミユリ海底譚,n-buna,Vocaloid,Easy 8,Standard 14,Hard 28,Master 46,Unlimited 64,
|
||||
100516,chalur,シャルル,バルーン,Vocaloid,Easy 14,Standard 18,Hard 40,Master 60,Unlimited 72,
|
||||
100517,melanc,メランコリック,Junky,Vocaloid,Easy 10,Standard 15,Hard 30,Master 50,Unlimited 63,
|
||||
100518,konofu,このふざけた素晴らしき世界は、僕の為にある,n.k,Vocaloid,Easy 11,Standard 23,Hard 35,Master 62,Unlimited 81,
|
||||
100526,bladem,The Blade Master,mikashu,Original,Easy 17,Standard 28,Hard 38,Master 63,Unlimited 75,
|
||||
100536,southw,South wind,moimoi,Original,Easy 12,Standard 18,Hard 27,Master 57,Unlimited 68,
|
||||
100537,ryuuse,流星デモクラシー,kamejack,Original,Easy 13,Standard 21,Hard 32,Master 59,Unlimited N/A,
|
||||
100519,redhea,ROCK'N'ROLL☆FLYING REDHEAD,暁Records,Touhou,Easy 10,Standard 27,Hard 39,Master 59,Unlimited 72,
|
||||
100520,warnin,WARNING×WARNING×WARNING,暁Records,Touhou,Easy 12,Standard 25,Hard 36,Master 61,Unlimited 74,
|
||||
100521,topsec,TOP SECRET -My Red World-,暁Records,Touhou,Easy 13,Standard 24,Hard 34,Master 51,Unlimited 64,
|
||||
100522,dddoll,DOWN DOWN DOLL,暁Records,Touhou,Easy 14,Standard 26,Hard 38,Master 55,Unlimited 63,
|
||||
100548,tracee,トレイス・エゴイズム,暁Records,Touhou,Easy 9,Standard 19,Hard 31,Master 49,Unlimited 65,
|
||||
100111,drivin,Driving story,Duca,Original,Easy 8,Standard 23,Hard 40,Master 66,Unlimited 76,
|
||||
100118,genzit,現実幻覚スピードスター,yozuca*,Original,Easy 12,Standard 18,Hard 46,Master 73,Unlimited 82,
|
||||
100039,aerial,エアリアル,カヒーナムジカ,Original,Easy 5,Standard 11,Hard 28,Master 56,Unlimited 71,
|
||||
100532,einher,Einherjar,閣下,Original,Easy 16,Standard 29,Hard 40,Master 74,Unlimited 80,
|
||||
100540,ariell,Ariel,nanobii,Original,Easy 15,Standard 19,Hard 32,Master 64,Unlimited 73,
|
||||
100542,firstl,First Love,UFO,Original,Easy 17,Standard 25,Hard 36,Master 65,Unlimited 77,
|
||||
100550,heartl,Heartland,Bernis,Original,Easy 11,Standard 23,Hard 30,Master 64,Unlimited N/A,
|
||||
100551,erasee,ERASE,MozSound,Original,Easy 12,Standard 22,Hard 35,Master 58,Unlimited 68,
|
||||
100530,regene,Regeneration ray,Tsukasa,Original,Easy 13,Standard 20,Hard 30,Master 56,Unlimited 70,
|
||||
100549,allelu,アレルヤ,HAKKYOU-KUN feat.玉置成実,Original,Easy 16,Standard 28,Hard 35,Master 64,Unlimited 75,
|
||||
100543,lighto,Light of my Life,S3RL,Original,Easy 12,Standard 25,Hard 33,Master 60,Unlimited 74,
|
||||
100552,termin,Terminus a quo,ginkiha,Original,Easy 13,Standard 24,Hard 34,Master 63,Unlimited 79,
|
||||
100556,ryuuse2,流星でもくらちー☆,kamejack,Original,Easy 13,Standard 20,Hard 36,Master 62,Unlimited 75,
|
||||
100547,prizmm,PRIZM,ミフメイ,Original,Easy 12,Standard 21,Hard 30,Master 54,Unlimited N/A,
|
||||
100098,samalv,サマ★ラブ,コツキミヤ,Original,Easy 13,Standard 19,Hard 39,Master 58,Unlimited 77,
|
||||
100544,palpit,Palpitation,Zekk,Original,Easy 18,Standard 29,Hard 55,Master 84,Unlimited 92,
|
||||
100558,gainen,Break the Wall!! ~ロンリガイネン,暁Records,Original,Easy 15,Standard 26,Hard 37,Master 63,Unlimited N/A,
|
||||
100525,moonsh,Moon Shard,satella,Original,Easy 10,Standard 23,Hard 36,Master 62,Unlimited N/A,
|
||||
100559,moonki,MoonLightKiss,effe,Original,Easy 13,Standard 25,Hard 39,Master 64,Unlimited N/A,
|
||||
100560,moonri,Moonrise,Relect,Original,Easy 14,Standard 21,Hard 38,Master 58,Unlimited 85,
|
||||
100561,goaway,Go Away,Cranky,Original,Easy 10,Standard 23,Hard 45,Master 59,Unlimited 70,
|
||||
100567,itback,Bring it back now,siromaru,Original,Easy 12,Standard 23,Hard 38,Master 71,Unlimited N/A,
|
||||
100569,redhhh,Red Heart,Yooh vs. siromaru,Original,Easy 13,Standard 24,Hard 39,Master 77,Unlimited N/A,
|
||||
100568,actual,Actual Reverse,siromaru,Original,Easy 14,Standard 25,Hard 38,Master 80,Unlimited N/A,
|
||||
100367,zonzon,Bi-Zon Zon Zombi,MC Natsack,Original,Easy 5,Standard 16,Hard 33,Master 63,Unlimited 67,
|
||||
100565,memorm,Memorim,Avans,Original,Easy 15,Standard 26,Hard 37,Master 73,Unlimited N/A,
|
||||
100554,kokoro,ココロメソッド,Endorfin.,Original,Easy 12,Standard 20,Hard 43,Master 65,Unlimited 69,
|
||||
100563,poweri,Power is Power,KO3,Original,Easy 13,Standard 26,Hard 49,Master 75,Unlimited 91,
|
||||
100555,nisenn,2020,Ω,Original,Easy N/A,Standard N/A,Hard N/A,Master 76,Unlimited N/A,
|
||||
100096,yukiya,Vespero,Monotone Rhythm feat.綾川雪弥,Original,Easy 11,Standard 19,Hard 40,Master 61,Unlimited N/A,
|
||||
100124,zankyo,残響のアカーシャ,Astilbe × arendsii,Original,Easy 10,Standard 18,Hard 38,Master 57,Unlimited 74,
|
||||
100119,overlp,オーバーラップ,millie loved by yksb,Original,Easy 9,Standard 17,Hard 30,Master 51,Unlimited N/A,
|
||||
100529,fracta,Fractalize,Sakuzyo,Original,Easy 19,Standard 31,Hard 52,Master 83,Unlimited N/A,
|
||||
100455,cantst,Can't Stop,KaSa,Original,Easy 11,Standard 23,Hard 42,Master 65,Unlimited N/A,
|
||||
100527,primaa,Prima,Kiryu(桐生),Original,Easy 12,Standard 18,Hard 35,Master 54,Unlimited 75,
|
||||
100448,cyberg,CYBER GANG,ヒゲドライVAN,Original,Easy 12,Standard 23,Hard 35,Master 60,Unlimited N/A,
|
||||
100018,freakw,Freak With Me,SLAKE,Original,Easy 13,Standard 22,Hard 42,Master 65,Unlimited 66,
|
||||
100006,aquali,Aqualight,MAYA AKAI,Original,Easy 11,Standard 16,Hard 34,Master 58,Unlimited N/A,
|
||||
100572,takesc,Music Takes Control,Fierce Chain,Original,Easy 10,Standard 27,Hard 37,Master 69,Unlimited N/A,
|
||||
100531,cthugh,Cthugha,MozSound,Original,Easy 14,Standard 25,Hard 48,Master 73,Unlimited N/A,
|
||||
100571,thetaa,θ (theta) ,effe,Original,Easy 11,Standard 21,Hard 34,Master 62,Unlimited N/A,
|
||||
100493,nekofu,ネコふんじゃった☆ (クローニャSTYLE),,Variety,Easy 10,Standard 22,Hard 34,Master 57,Unlimited 80,
|
||||
100057,howtru,How True Is Your Love,brinq,Original,Easy 8,Standard 12,Hard 25,Master 53,Unlimited 74,
|
||||
100047,romanc,ロマンシングゲーム,まふ×ティン,Original,Easy 10,Standard 28,Hard 55,Master 78,Unlimited N/A,
|
||||
100573,kotobu,KOTOBUKI,REVen-G,Original,Easy 25,Standard 32,Hard 71,Master 90,Unlimited N/A,
|
||||
100417,xmasss,ジングルベル (NM REMIX),,Variety,Easy 8,Standard 18,Hard 38,Master 56,Unlimited 77,
|
||||
100600,galaxy,GALAXY,キュウソネコカミ,J-Pop,Easy 10,Standard 16,Hard 32,Master 43,Unlimited 67,
|
||||
100601,rebell,Rebellion,NAOKI underground,Original,Easy N/A,Standard 49,Hard 63,Master 91,Unlimited N/A,
|
||||
100602,anothe,Another Chance,Luci,Original,Easy N/A,Standard 27,Hard 37,Master 73,Unlimited 76,
|
||||
100603,addict,Addicted,luz×アリエP,Original,Easy N/A,Standard 20,Hard 34,Master 52,Unlimited 62,
|
||||
100604,dirtyy,Dirty Mouth,Asletics,Original,Easy N/A,Standard 15,Hard 28,Master 59,Unlimited 74,
|
||||
100605,levelf,LEVEL5-Judgelight-,fripSide,J-Pop,Easy 5,Standard 11,Hard 28,Master 45,Unlimited 63,
|
||||
100606,omnive,Omniverse,Atomic,Original,Easy N/A,Standard 34,Hard 52,Master 83,Unlimited 86,
|
||||
100607,kakuse,覚醒 ∞ awake!,PwD,Original,Easy N/A,Standard 17,Hard 55,Master 75,Unlimited N/A,
|
||||
100608,unbeli,アンビリーバーズ,米津玄師,J-Pop,Easy 7,Standard 13,Hard 26,Master 38,Unlimited 62,
|
||||
100609,sonzai,ソンザイキョウドウタイ,ジギル,Original,Easy N/A,Standard 26,Hard 40,Master 59,Unlimited 66,
|
||||
100610,okonik,OKONIKUKOD,SHU OKUYAMA,Original,Easy N/A,Standard 26,Hard 45,Master 67,Unlimited N/A,
|
||||
100611,crssho,CrossShooter,Tatsh,Original,Easy 10,Standard 35,Hard 60,Master 85,Unlimited N/A,
|
||||
100612,reanim,Reanimation,DC feat.S!N,Original,Easy N/A,Standard 28,Hard 44,Master 70,Unlimited 80,
|
||||
100613,kamino,kaminoko,HAKKYOU-KUN,Original,Easy 15,Standard 40,Hard 62,Master 78,Unlimited N/A,
|
||||
100614,fiveee,Five,ANOTHER STORY,J-Pop,Easy 10,Standard 18,Hard 37,Master 61,Unlimited 71,
|
||||
100615,granda,Grand Arc,Tosh,Original,Easy N/A,Standard 21,Hard 38,Master 79,Unlimited N/A,
|
||||
100616,fronti2,NEXT FRONTIER -TRUE RISE-,NAOKI,Original,Easy 9,Standard 46,Hard 69,Master 89,Unlimited N/A,
|
||||
100617,saigon,最後の1ページ,桜井零士,Original,Easy N/A,Standard 19,Hard 31,Master 57,Unlimited N/A,
|
||||
100618,replay,REPLAY,VAMPS,J-Pop,Easy 8,Standard 18,Hard 44,Master 63,Unlimited 70,
|
||||
100619,mousou,妄想全開,志麻×ふぉP,Original,Easy N/A,Standard 16,Hard 26,Master 54,Unlimited N/A,
|
||||
100620,aheadd,AHEAD,VAMPS,J-Pop,Easy 7,Standard 13,Hard 25,Master 35,Unlimited 58,
|
||||
100621,musicr1,All You Need Is Beat(s) -musicるTV・ミリオン連発音楽作家塾第7弾-,CLONE,Original,Easy 12,Standard 22,Hard 33,Master 58,Unlimited 74,
|
||||
100622,getthe,Get the glory,中ノ森文子,J-Pop,Easy 6,Standard 17,Hard 37,Master 49,Unlimited 66,
|
||||
100623,design,Designed World,Alinut,Original,Easy N/A,Standard 15,Hard 39,Master 68,Unlimited 69,
|
||||
100624,garnet,GARNET HOWL,フラット3rd,Original,Easy N/A,Standard 26,Hard 46,Master 70,Unlimited 94,
|
||||
100625,hopesb,Hopes Bright,WHITE ASH,J-Pop,Easy 7,Standard 10,Hard 25,Master 44,Unlimited 61,
|
||||
100626,shooti,Shooting Star feat.HISASHI (GLAY),96猫,J-Pop,Easy 7,Standard 15,Hard 37,Master 49,Unlimited 69,
|
||||
100627,dangan,弾丸と星空,HAKKYOU-KUN,Original,Easy N/A,Standard 28,Hard 58,Master 81,Unlimited N/A,
|
||||
100628,impact,Impact,Tatsh,Original,Easy 20,Standard 24,Hard 60,Master 72,Unlimited 90,
|
||||
100629,lightm,Light My Fire,KOTOKO,J-Pop,Easy 11,Standard 26,Hard 33,Master 54,Unlimited 71,
|
||||
100630,miiroo,海色,AKINO from bless4,J-Pop,Easy 11,Standard 22,Hard 39,Master 58,Unlimited 68,
|
||||
100631,voiceo,Voice Of House,DOT96,Original,Easy N/A,Standard 18,Hard 34,Master 58,Unlimited 59,
|
||||
100632,cosmol,Cosmology,RIC,Original,Easy 25,Standard 36,Hard 64,Master 87,Unlimited N/A,
|
||||
100633,vividd,ViViD,May'n,J-Pop,Easy 9,Standard 16,Hard 35,Master 55,Unlimited 65,
|
||||
100634,splash,SPLASH,MAYA AKAI,Original,Easy N/A,Standard 26,Hard 50,Master 71,Unlimited N/A,
|
||||
100635,donuth,ドーナツホール,ハチ,Vocaloid,Easy 11,Standard 22,Hard 40,Master 54,Unlimited 80,
|
||||
100636,senbon,千本桜,和楽器バンド,Vocaloid,Easy 12,Standard 20,Hard 28,Master 54,Unlimited 74,
|
||||
100637,kmtyju,君と野獣,バンドハラスメント,J-Pop,Easy 12,Standard 24,Hard 31,Master 57,Unlimited 74,
|
||||
100638,fronti,NEXT FRONTIER,NAOKI,Original,Easy 13,Standard 48,Hard 65,Master 82,Unlimited N/A,
|
||||
100639,nueraa,Nu Era,SPARKER,Original,Easy N/A,Standard 22,Hard 43,Master 75,Unlimited 53,
|
||||
100640,childe,CHiLD -error-,MY FIRST STORY,J-Pop,Easy 4,Standard 9,Hard 24,Master 34,Unlimited 56,
|
||||
100641,dazzli2,DAZZLING♡SEASON (Darwin Remix),jun,Original,Easy 19,Standard 35,Hard 60,Master 82,Unlimited N/A,
|
||||
100642,perfec,Perfectionism,高橋渉 feat.2d6,Original,Easy N/A,Standard 39,Hard 64,Master 78,Unlimited N/A,
|
||||
100643,flower,Flowerwall,米津玄師,J-Pop,Easy 6,Standard 7,Hard 20,Master 40,Unlimited 65,
|
||||
100644,frgmnt,Frgmnts,Nyolfen,Original,Easy 10,Standard 33,Hard 63,Master 74,Unlimited 65,
|
||||
100645,headph,HEADPHONE PARTY,A-One,Original,Easy N/A,Standard 24,Hard 32,Master 52,Unlimited N/A,
|
||||
100646,crsang,Cross+Angel,Tatsh feat. 彩音,Original,Easy 13,Standard 27,Hard 53,Master 67,Unlimited N/A,
|
||||
100647,musicr4,Accept,sushi feat.とよだま,Original,Easy 12,Standard 19,Hard 32,Master 58,Unlimited N/A,
|
||||
100648,imaxim,A×E×U×G -act.1-,190Cb,Original,Easy N/A,Standard 44,Hard 69,Master 90,Unlimited 87,
|
||||
100649,azitat2,Azitate (Prologue Edition),void,Original,Easy 8,Standard 23,Hard 52,Master 66,Unlimited N/A,
|
||||
100650,dynami,DYNAMITE SENSATION,NAOKI,Original,Easy 11,Standard 26,Hard 54,Master 68,Unlimited N/A,
|
||||
100651,incave,Into the Cave,Jerico,Original,Easy N/A,Standard 22,Hard 44,Master 76,Unlimited 78,
|
||||
100652,aktuki,AKATSUKI,NAOKI underground,Original,Easy 10,Standard 26,Hard 58,Master 84,Unlimited N/A,
|
||||
100653,kindof,Wonderful,Fraz,Original,Easy N/A,Standard 14,Hard 29,Master 48,Unlimited N/A,
|
||||
100654,mikaku,未確認XX生命体,民安★ROCK,Original,Easy N/A,Standard 19,Hard 31,Master 54,Unlimited N/A,
|
||||
100655,strang,ストレンジ・ディーヴァ,麹町養蚕館,Original,Easy N/A,Standard 12,Hard 28,Master 55,Unlimited N/A,
|
||||
100656,hesper,Hesperides,xi,Original,Easy N/A,Standard 36,Hard 61,Master 92,Unlimited 93,
|
||||
100657,breaka,Break a spell,川田まみ,J-Pop,Easy 7,Standard 15,Hard 31,Master 45,Unlimited 68,
|
||||
100658,myname,When You Call My Name,Beat Envy,Original,Easy N/A,Standard 6,Hard 14,Master 30,Unlimited 57,
|
||||
100659,amaiko,甘い言葉,Kenichi Chiba feat. EVO+,Original,Easy N/A,Standard 15,Hard 37,Master 60,Unlimited N/A,
|
||||
100660,reseed2,Reseed,quick master,Original,Easy N/A,Standard 22,Hard 47,Master 63,Unlimited N/A,
|
||||
100661,kingst,KING STUN,JUPITRIS,Original,Easy 12,Standard 38,Hard 63,Master 74,Unlimited N/A,
|
||||
100662,ramram,Break Your World,RAM,Original,Easy N/A,Standard 23,Hard 34,Master 67,Unlimited N/A,
|
||||
100663,murasa,Murasame,Ryunosuke Kudo,Original,Easy N/A,Standard 28,Hard 41,Master 76,Unlimited N/A,
|
||||
100664,happyd,Happy Deathday,ANOTHER STORY,Original,Easy 18,Standard 22,Hard 41,Master 73,Unlimited 79,
|
||||
100665,izimed,イジメ、ダメ、ゼッタイ,BABYMETAL,J-Pop,Easy 9,Standard 19,Hard 39,Master 69,Unlimited 77,
|
||||
100666,wastel,Wasteland,James Taplin,Original,Easy N/A,Standard 4,Hard 12,Master 23,Unlimited 40,
|
||||
100667,assign,Assign,MASAYASU,Original,Easy N/A,Standard 26,Hard 43,Master 61,Unlimited 62,
|
||||
100668,jahaci,Jahacid,DJ SODEYAMA,Original,Easy N/A,Standard 17,Hard 29,Master 59,Unlimited N/A,
|
||||
100669,hisuii,Hisui,stereoberry,Original,Easy N/A,Standard 22,Hard 47,Master 70,Unlimited N/A,
|
||||
100670,godkno,God knows...,涼宮ハルヒ(C.V.平野綾),J-Pop,Easy 6,Standard 10,Hard 26,Master 45,Unlimited 64,
|
||||
100671,roadof,Road of Resistance,BABYMETAL,J-Pop,Easy 7,Standard 15,Hard 36,Master 50,Unlimited 75,
|
||||
100672,rokuch,六兆年と一夜物語,和楽器バンド,J-Pop + Vocaloid,Easy 11,Standard 21,Hard 35,Master 62,Unlimited 81,
|
||||
100673,valent,いつか王子様が (Remix Ver.),,Original,Easy 10,Standard 27,Hard 33,Master 59,Unlimited 77,
|
||||
100674,unfini,→unfinished→,KOTOKO,J-Pop,Easy 8,Standard 16,Hard 32,Master 50,Unlimited 71,
|
||||
100675,auflcb2,some day -see you again-,NAOKI,Original,Easy 10,Standard 22,Hard 37,Master 75,Unlimited N/A,
|
||||
100676,burnin,Burning Inside,Nhato,Original,Easy 15,Standard 18,Hard 28,Master 60,Unlimited 85,
|
||||
100677,sphere,Hypersphere,Dubscribe,Original,Easy N/A,Standard 20,Hard 38,Master 73,Unlimited N/A,
|
||||
100678,dropou,D.O.B.,野水いおり,J-Pop,Easy 14,Standard 17,Hard 31,Master 46,Unlimited 69,
|
||||
100679,xencou,X-encounter,黒崎真音,J-Pop,Easy 8,Standard 20,Hard 32,Master 52,Unlimited 60,
|
||||
100680,killyk,killy killy JOKER,分島花音,J-Pop,Easy 6,Standard 13,Hard 42,Master 63,Unlimited 76,
|
||||
100681,missil,the Last Missile Man,adHoc World,Original,Easy N/A,Standard 16,Hard 38,Master 59,Unlimited N/A,
|
||||
100682,burstt,Burst The Gravity,ALTIMA,J-Pop,Easy 7,Standard 12,Hard 25,Master 46,Unlimited 63,
|
||||
100683,musicr2,My Recklessness,Kagerou,Original,Easy 12,Standard 22,Hard 33,Master 58,Unlimited N/A,
|
||||
100684,isingl,Isinglass,Voltex,Original,Easy 12,Standard 25,Hard 44,Master 80,Unlimited N/A,
|
||||
100685,lvless,Loveless,YOSA,Original,Easy N/A,Standard 23,Hard 38,Master 60,Unlimited N/A,
|
||||
100686,sapphi,Sapphire,voltex,Original,Easy N/A,Standard 29,Hard 44,Master 81,Unlimited N/A,
|
||||
100687,musicr3,Climaxxx Party -musicるTV・ミリオン連発音楽作家塾第7弾-,Kyota. feat.とよだま&れい,Original,Easy 12,Standard 19,Hard 32,Master 58,Unlimited 72,
|
||||
100688,deeout,Deep Outside,Seiho,Original,Easy N/A,Standard 18,Hard 34,Master 63,Unlimited 81,
|
||||
100689,sugars,シュガーソングとビターステップ,UNISON SQUARE GARDEN,J-Pop,Easy 6,Standard 17,Hard 30,Master 42,Unlimited 66,
|
||||
100690,mercur,MERCURY ,E.Z.M,Original,Easy N/A,Standard 14,Hard 35,Master 66,Unlimited N/A,
|
||||
100691,zizizi,Z[i],Cybermiso,Original,Easy N/A,Standard 30,Hard 57,Master 88,Unlimited 96,
|
||||
100692,wegooo,WE GO,BREAKERZ,J-Pop,Easy 10,Standard 18,Hard 34,Master 54,Unlimited 68,
|
||||
100693,alonee,ALONE,MY FIRST STORY,J-Pop,Easy 5,Standard 11,Hard 21,Master 36,Unlimited 48,
|
||||
100694,nuheat,Nu Heat,Paisley Parks,Original,Easy N/A,Standard 29,Hard 44,Master 65,Unlimited 85,
|
||||
100695,granro,メモリーズ,GRANRODEO,J-Pop,Easy 8,Standard 15,Hard 28,Master 43,Unlimited 60,
|
||||
100696,sister,sister's noise,fripSide,J-Pop,Easy 7,Standard 10,Hard 27,Master 46,Unlimited 63,
|
||||
100697,lotusl,Lotus Love,Maozon,Original,Easy N/A,Standard 20,Hard 36,Master 64,Unlimited N/A,
|
||||
100698,yukari,YUKARI,Ocelot,Original,Easy N/A,Standard 31,Hard 50,Master 76,Unlimited 84,
|
||||
100699,flawli,フローライト,米津玄師,J-Pop,Easy 8,Standard 17,Hard 30,Master 40,Unlimited 59,
|
||||
100700,nightf,NIGHT FEELIN',マセラティ渚,Original,Easy N/A,Standard 15,Hard 28,Master 46,Unlimited 71,
|
||||
100701,random,シャッフルセレクト,シャッフルセレクト,Original,Easy N/A,Standard N/A,Hard N/A,Master N/A,Unlimited N/A,
|
||||
100702,wiwwtw,What Is Wrong With The World,SADA,Original,Easy N/A,Standard 26,Hard 38,Master 62,Unlimited N/A,
|
||||
100703,inneru,Inner Urge,上坂すみれ,Original,Easy 9,Standard 22,Hard 36,Master 48,Unlimited 67,
|
||||
100704,taishi,Otherside,Taishi,Original,Easy N/A,Standard 19,Hard 35,Master 58,Unlimited N/A,
|
||||
100705,daysss,Days,Kent Alexander,Original,Easy N/A,Standard 38,Hard 59,Master 81,Unlimited 81,
|
||||
100706,bokuwa,僕は君のアジテーターじゃない feat.Neru,焚吐,J-Pop,Easy 16,Standard 23,Hard 34,Master 55,Unlimited 69,
|
||||
100707,showww,掌 -show-,喜多村英梨,Original,Easy 15,Standard 18,Hard 35,Master 51,Unlimited 79,
|
||||
100708,nevers,Never Sleep Again,PassCode,J-Pop,Easy 15,Standard 26,Hard 32,Master 65,Unlimited 75,
|
||||
100709,bleeze,BLEEZE,GLAY,J-Pop,Easy 9,Standard 16,Hard 31,Master 47,Unlimited 62,
|
||||
100710,dreami,DREAMIN' OF YOU feat.コッテル,Arts Of Collective,Original,Easy N/A,Standard 14,Hard 37,Master 65,Unlimited N/A,
|
||||
100711,allune,All U Need,MesoPhunk,Pick-Up (New + Revival),Easy N/A,Standard 14,Hard 35,Master 71,Unlimited N/A,
|
||||
100712,always,Always Thinking Of You,Sketchout,Pick-Up (New + Revival),Easy N/A,Standard 13,Hard 27,Master 49,Unlimited N/A,
|
||||
100713,anomie2,Anomie (Axiom Style),D-Fener,Pick-Up (New + Revival),Easy N/A,Standard 16,Hard 43,Master 84,Unlimited N/A,
|
||||
100714,aquali2,Aqualight (Remix Ver.),MAYA AKAI,Pick-Up (New + Revival),Easy N/A,Standard 22,Hard 43,Master 60,Unlimited 81,
|
||||
100715,astaro,ASTAROTH,JUPITRIS,Pick-Up (New + Revival),Easy N/A,Standard 23,Hard 40,Master 74,Unlimited N/A,
|
||||
100716,bassan,BASS ANTICS,Mitomoro,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 32,Master 66,Unlimited N/A,
|
||||
100717,zonzon2,Bi-Zon Zon Zombi (More Zombies Ver.),MC Natsack,Pick-Up (New + Revival),Easy N/A,Standard 13,Hard 27,Master 68,Unlimited 75,
|
||||
100718,bouled,boule de berlin,JTTR,Pick-Up (New + Revival),Easy N/A,Standard 19,Hard 30,Master 57,Unlimited N/A,
|
||||
100719,brandn,BRAND NEW,Headphone-Tokyo(star)(star) feat.カヒーナ,Pick-Up (New + Revival),Easy N/A,Standard 9,Hard 39,Master 66,Unlimited 72,
|
||||
100720,bravee,BRAVE,Ryuno,Pick-Up (New + Revival),Easy N/A,Standard 35,Hard 60,Master 82,Unlimited N/A,
|
||||
100721,breakd2,Break down (2nd Edition),GARNiDELiA,Pick-Up (New + Revival),Easy N/A,Standard 34,Hard 64,Master 74,Unlimited N/A,
|
||||
100722,buffet,Buffet survivor,Yamajet feat. Cathy & TEA,Pick-Up (New + Revival),Easy N/A,Standard 38,Hard 55,Master 68,Unlimited N/A,
|
||||
100723,buzzke,BUZZ Ketos,フラット3rd,Pick-Up (New + Revival),Easy N/A,Standard 18,Hard 33,Master 58,Unlimited 77,
|
||||
100724,cashhh,Cash!,Nor,Pick-Up (New + Revival),Easy N/A,Standard 19,Hard 25,Master 64,Unlimited N/A,
|
||||
100725,cloudb,Cloudburst,Relect,Pick-Up (New + Revival),Easy N/A,Standard 37,Hard 66,Master 74,Unlimited N/A,
|
||||
100726,clouds,cloudstepping,Ryuno,Pick-Up (New + Revival),Easy N/A,Standard 13,Hard 25,Master 47,Unlimited N/A,
|
||||
100727,codepa,Code Paradiso,Himmel,Pick-Up (New + Revival),Easy N/A,Standard 29,Hard 55,Master 70,Unlimited N/A,
|
||||
100728,comear,Come Around,MesoPhunk,Pick-Up (New + Revival),Easy N/A,Standard 38,Hard 56,Master 83,Unlimited N/A,
|
||||
100729,crysta,Crystal Ribbon,Cosine,Pick-Up (New + Revival),Easy N/A,Standard 37,Hard 56,Master 81,Unlimited N/A,
|
||||
100730,curseo,Curse of Doll,KO3,Pick-Up (New + Revival),Easy N/A,Standard 22,Hard 36,Master 74,Unlimited N/A,
|
||||
100731,datami,data mining,voia,Pick-Up (New + Revival),Easy N/A,Standard 18,Hard 36,Master 66,Unlimited N/A,
|
||||
100732,defaul,default affinity,JTTR,Pick-Up (New + Revival),Easy N/A,Standard 21,Hard 33,Master 48,Unlimited N/A,
|
||||
100733,design2,Designed World (Remix ver.),Alinut,Pick-Up (New + Revival),Easy N/A,Standard 25,Hard 43,Master 68,Unlimited N/A,
|
||||
100734,diamon,DIAMOND SKIN,GLAY,Pick-Up (New + Revival),Easy N/A,Standard 10,Hard 26,Master 33,Unlimited N/A,
|
||||
100735,dispel,dispel,Endorfin.,Pick-Up (New + Revival),Easy N/A,Standard 28,Hard 48,Master 80,Unlimited N/A,
|
||||
100736,distan,Distantmemory,村瀬悠太,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 30,Master 68,Unlimited N/A,
|
||||
100737,dokibl,Doki Blaster,VOIA,Pick-Up (New + Revival),Easy N/A,Standard 15,Hard 23,Master 67,Unlimited N/A,
|
||||
100738,dontwa,Don't Walk Away,Sarah-Jane,Pick-Up (New + Revival),Easy N/A,Standard 13,Hard 34,Master 69,Unlimited N/A,
|
||||
100739,drgirl,Dreaming Girl,Nor,Pick-Up (New + Revival),Easy N/A,Standard 19,Hard 35,Master 54,Unlimited 73,
|
||||
100740,eterna,Eternally,GLAY,Pick-Up (New + Revival),Easy N/A,Standard 12,Hard 21,Master 39,Unlimited N/A,
|
||||
100741,everkr,everKrack,GLAY,Pick-Up (New + Revival),Easy N/A,Standard 18,Hard 29,Master 41,Unlimited N/A,
|
||||
100742,everwh,EverWhite,satella,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 31,Master 58,Unlimited N/A,
|
||||
100743,farthe,FarthestEnd,Sakuzyo,Pick-Up (New + Revival),Easy N/A,Standard 30,Hard 56,Master 78,Unlimited 87,
|
||||
100744,filame,Filament Flow,Endorfin.,Pick-Up (New + Revival),Easy N/A,Standard 23,Hard 38,Master 63,Unlimited N/A,
|
||||
100745,flameu2,Flame Up (Remix Ver.),Inu Machine,Pick-Up (New + Revival),Easy N/A,Standard 17,Hard 24,Master 59,Unlimited N/A,
|
||||
100746,freeee,Free,千π,Pick-Up (New + Revival),Easy N/A,Standard 19,Hard 39,Master 69,Unlimited N/A,
|
||||
100747,funkyb2,FUNKYBABY EVOLUTION,Yamajet,Pick-Up (New + Revival),Easy N/A,Standard 21,Hard 34,Master 56,Unlimited N/A,
|
||||
100748,granda2,Grand Arc (Club Remix),Tosh,Pick-Up (New + Revival),Easy N/A,Standard 24,Hard 41,Master 73,Unlimited 83,
|
||||
100749,hsphsp,H.S.P (Hard Style Party),Ravine & Tom Budin,Pick-Up (New + Revival),Easy N/A,Standard 12,Hard 25,Master 69,Unlimited N/A,
|
||||
100750,halluc,Hallucination XXX,t+pazolite,Pick-Up (New + Revival),Easy N/A,Standard 40,Hard 52,Master 87,Unlimited N/A,
|
||||
100751,indigo,Indigo Isle,Syntax Error,Pick-Up (New + Revival),Easy N/A,Standard 17,Hard 33,Master 50,Unlimited 75,
|
||||
100752,inters,Interstellar Plazma,KO3,Pick-Up (New + Revival),Easy N/A,Standard 25,Hard 42,Master 77,Unlimited N/A,
|
||||
100753,incave2,Into the Cave (Another Edit),Jerico,Pick-Up (New + Revival),Easy N/A,Standard 31,Hard 57,Master 88,Unlimited N/A,
|
||||
100754,ioniza,IONIZATION,llliiillliiilll,Pick-Up (New + Revival),Easy N/A,Standard 17,Hard 34,Master 70,Unlimited 85,
|
||||
100755,guilty,JUSTICE [from] GUILTY,GLAY,Pick-Up (New + Revival),Easy N/A,Standard 15,Hard 28,Master 50,Unlimited N/A,
|
||||
100756,keraun,Keraunos,Xiphoid Sphere (xi + siromaru),Pick-Up (New + Revival),Easy N/A,Standard 25,Hard 52,Master 79,Unlimited N/A,
|
||||
100757,landin2,Landing on the moon (Instrumental Version),SIMON,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 34,Master 59,Unlimited 66,
|
||||
100758,videog,Life In A Video Game,Bentobox,Pick-Up (New + Revival),Easy N/A,Standard 21,Hard 37,Master 62,Unlimited N/A,
|
||||
100759,loseyo,Lose Your Mind,Vau Boy,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 30,Master 71,Unlimited N/A,
|
||||
100760,machin,Machine,Sparky,Pick-Up (New + Revival),Easy N/A,Standard 12,Hard 28,Master 72,Unlimited N/A,
|
||||
100761,makeit,Make It Fresh EDM ver.,HighLux,Pick-Up (New + Revival),Easy N/A,Standard 11,Hard 24,Master 48,Unlimited N/A,
|
||||
100762,daydre,Mechanized Daydream,s-don,Pick-Up (New + Revival),Easy N/A,Standard 19,Hard 36,Master 80,Unlimited N/A,
|
||||
100763,metron,Metro Night,ginkiha,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 44,Master 71,Unlimited N/A,
|
||||
100764,milkyw,Milky Way Trip,Nor,Pick-Up (New + Revival),Easy N/A,Standard 22,Hard 31,Master 60,Unlimited N/A,
|
||||
100766,nayuta,nayuta,happy machine,Pick-Up (New + Revival),Easy N/A,Standard 17,Hard 37,Master 68,Unlimited N/A,
|
||||
100767,nightm,nightmares,Seeds of the Upcoming Infection,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 49,Master 73,Unlimited N/A,
|
||||
100768,otherw,Other World,XIO,Pick-Up (New + Revival),Easy N/A,Standard 23,Hard 41,Master 76,Unlimited N/A,
|
||||
100769,overth,Over The Blue (Breaking Through),Fracus & Darwin Feat. Jenna,Pick-Up (New + Revival),Easy N/A,Standard 33,Hard 57,Master 82,Unlimited N/A,
|
||||
100770,uuuuuu,Phoenix,U,Pick-Up (New + Revival),Easy N/A,Standard 23,Hard 37,Master 74,Unlimited N/A,
|
||||
100771,rainin,Raining Again feat. Bea Aria,Sanxion,Pick-Up (New + Revival),Easy N/A,Standard 16,Hard 41,Master 69,Unlimited N/A,
|
||||
100772,raisey,Raise Your Handz!,KO3 & Relect,Pick-Up (New + Revival),Easy N/A,Standard 23,Hard 55,Master 75,Unlimited N/A,
|
||||
100773,resona,Resonance,RAM,Pick-Up (New + Revival),Easy N/A,Standard 17,Hard 32,Master 64,Unlimited N/A,
|
||||
100774,reuniv,Reuniverse,Headphone-Tokyo(star)(star) feat.カヒーナ,Pick-Up (New + Revival),Easy N/A,Standard 14,Hard 23,Master 41,Unlimited N/A,
|
||||
100775,rhythm,RHYTHM GAME MACHINE,ginkiha,Pick-Up (New + Revival),Easy N/A,Standard 37,Hard 56,Master 78,Unlimited N/A,
|
||||
100776,rushhh,Rush,TANUKI,Pick-Up (New + Revival),Easy N/A,Standard 25,Hard 37,Master 75,Unlimited N/A,
|
||||
100777,steeee,S.T.E,Tatsh,Pick-Up (New + Revival),Easy N/A,Standard 30,Hard 58,Master 87,Unlimited N/A,
|
||||
100778,sangey,Sangeyasya,NNNNNNNNNN,Pick-Up (New + Revival),Easy N/A,Standard 27,Hard 47,Master 85,Unlimited N/A,
|
||||
100779,senpai,Senpai Slam,千π,Pick-Up (New + Revival),Easy N/A,Standard 38,Hard 54,Master 77,Unlimited N/A,
|
||||
100780,sestea,Sestea,Feryquitous,Pick-Up (New + Revival),Easy N/A,Standard 27,Hard 47,Master 76,Unlimited N/A,
|
||||
100781,silver,Silverd,Feryquitous,Pick-Up (New + Revival),Easy N/A,Standard 28,Hard 40,Master 69,Unlimited N/A,
|
||||
100782,sodama,Soda Machine,Syntax Error,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 40,Master 65,Unlimited N/A,
|
||||
100783,stardu,STARDUST (game edit),MINIKOMA★,Pick-Up (New + Revival),Easy N/A,Standard 19,Hard 33,Master 64,Unlimited N/A,
|
||||
100784,starti,starting station,happy machine,Pick-Up (New + Revival),Easy N/A,Standard 17,Hard 31,Master 54,Unlimited 70,
|
||||
100785,sunday,SUNDAY リベンジ,HAPPY SUNDAY,Pick-Up (New + Revival),Easy N/A,Standard 18,Hard 29,Master 46,Unlimited 67,
|
||||
100786,sundro2,Sundrop (Remix ver.),Yamajet,Pick-Up (New + Revival),Easy N/A,Standard 30,Hard 48,Master 79,Unlimited 82,
|
||||
100787,sunnyd,Sunny day,センラ×蒼炎P,Pick-Up (New + Revival),Easy N/A,Standard 23,Hard 38,Master 59,Unlimited N/A,
|
||||
100788,superl,SuperLuminalGirl Rebirth,Yamajet feat. 小宮真央,Pick-Up (New + Revival),Easy N/A,Standard 15,Hard 32,Master 59,Unlimited N/A,
|
||||
100789,switch,SW!TCH,千π & MesoPhunk,Pick-Up (New + Revival),Easy N/A,Standard 16,Hard 35,Master 69,Unlimited N/A,
|
||||
100790,theepi2,The Epic -Introduction-,Cranky,Pick-Up (New + Revival),Easy N/A,Standard 22,Hard 37,Master 65,Unlimited N/A,
|
||||
100791,epipha,The Epiphany of Hardcore,SOTUI,Pick-Up (New + Revival),Easy N/A,Standard 15,Hard 30,Master 70,Unlimited N/A,
|
||||
100792,thekin,The King of Pirates,RiraN,Pick-Up (New + Revival),Easy N/A,Standard 22,Hard 52,Master 75,Unlimited N/A,
|
||||
100793,timele,Timeless encode,Vice Principal,Pick-Up (New + Revival),Easy N/A,Standard 16,Hard 33,Master 72,Unlimited N/A,
|
||||
100794,tokyoo,tokyo,Headphone-Tokyo(star)(star) feat.nayuta,Pick-Up (New + Revival),Easy N/A,Standard 15,Hard 33,Master 71,Unlimited N/A,
|
||||
100795,toooma,Tooo Many,S3RL,Pick-Up (New + Revival),Easy N/A,Standard 30,Hard 51,Master 77,Unlimited N/A,
|
||||
100796,toucho2,Touch Of Gold (Bongo Mango Remix),Togo Project feat. Frances Maya,Pick-Up (New + Revival),Easy N/A,Standard 17,Hard 32,Master 52,Unlimited 78,
|
||||
100797,tayuta,tΔyutΔi,ミフメイ,Pick-Up (New + Revival),Easy N/A,Standard 26,Hard 35,Master 72,Unlimited N/A,
|
||||
100798,ultrix,ULTRiX,sky_delta,Pick-Up (New + Revival),Easy N/A,Standard 27,Hard 45,Master 76,Unlimited N/A,
|
||||
100799,underw,Underworld,ANOTHER STORY,Pick-Up (New + Revival),Easy N/A,Standard 29,Hard 46,Master 69,Unlimited 86,
|
||||
100800,virtua,Virtual Reality Controller,フラット3rd,Pick-Up (New + Revival),Easy N/A,Standard 15,Hard 35,Master 63,Unlimited N/A,
|
||||
100801,voiceo2,VOICE OF HOUSE (96TH RETROMAN REMIX),DOT96,Pick-Up (New + Revival),Easy N/A,Standard 14,Hard 38,Master 67,Unlimited N/A,
|
||||
100802,wannab2,Wanna Be Your Special (Remix ver.),Shoichiro Hirata feat. SUIMI,Pick-Up (New + Revival),Easy N/A,Standard 26,Hard 41,Master 69,Unlimited N/A,
|
||||
100803,wiwwtw2,What Is Wrong With The World (Cross Edit),SADA,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 43,Master 67,Unlimited 72,
|
||||
100804,wingso,Wings of Twilight,sky_delta,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 53,Master 71,Unlimited N/A,
|
||||
100805,winter,Winter again,GLAY,Pick-Up (New + Revival),Easy N/A,Standard 14,Hard 24,Master 41,Unlimited N/A,
|
||||
100806,iineee,いいね!,BABYMETAL,Pick-Up (New + Revival),Easy N/A,Standard 21,Hard 40,Master 81,Unlimited N/A,
|
||||
100807,illumi,イルミナレガロ,Headphone-Tokyo(star)(star) feat.MiLO,Pick-Up (New + Revival),Easy N/A,Standard 10,Hard 25,Master 46,Unlimited 63,
|
||||
100808,yellll,エール,FullMooN,Pick-Up (New + Revival),Easy N/A,Standard 8,Hard 17,Master 52,Unlimited N/A,
|
||||
100809,eschat,エスカトロジィ,MozSound,Pick-Up (New + Revival),Easy N/A,Standard 36,Hard 57,Master 77,Unlimited N/A,
|
||||
100810,counte,カウンターストップ,フラット3rd,Pick-Up (New + Revival),Easy N/A,Standard 29,Hard 34,Master 71,Unlimited N/A,
|
||||
100811,gimcho,ギミチョコ!!,BABYMETAL,Pick-Up (New + Revival),Easy N/A,Standard 18,Hard 39,Master 70,Unlimited N/A,
|
||||
100812,surviv,サバイバル,GLAY,Pick-Up (New + Revival),Easy N/A,Standard 24,Hard 40,Master 65,Unlimited N/A,
|
||||
100814,turkis3,トルコ行進曲 (Short Remix),,Pick-Up (New + Revival),Easy N/A,Standard 6,Hard 20,Master 48,Unlimited N/A,
|
||||
100815,picora2,ピコラセテ (Instrumental Ver.),TORIENA,Pick-Up (New + Revival),Easy N/A,Standard 28,Hard 53,Master 80,Unlimited N/A,
|
||||
100816,fortis,フォルテシモ,らむだーじゃん,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 37,Master 53,Unlimited N/A,
|
||||
100817,hedban,ヘドバンギャー!!,BABYMETAL,Pick-Up (New + Revival),Easy N/A,Standard 16,Hard 43,Master 66,Unlimited N/A,
|
||||
100818,megitu,メギツネ,BABYMETAL,Pick-Up (New + Revival),Easy N/A,Standard 15,Hard 30,Master 49,Unlimited N/A,
|
||||
100819,rockma,ロックマン (CUTMAN STAGE),Remixed by てつ×ねこ,Pick-Up (New + Revival),Easy N/A,Standard 27,Hard 48,Master 73,Unlimited N/A,
|
||||
100820,kounen2,光年(konen)-Remix Ver.-,小野秀幸,Pick-Up (New + Revival),Easy N/A,Standard 21,Hard 43,Master 73,Unlimited N/A,
|
||||
100821,saisyu,最終回STORY,MY FIRST STORY,Pick-Up (New + Revival),Easy N/A,Standard 18,Hard 36,Master 56,Unlimited N/A,
|
||||
100822,yuukan,勇敢 i tout,kamejack,Pick-Up (New + Revival),Easy N/A,Standard 22,Hard 33,Master 78,Unlimited N/A,
|
||||
100823,modern,彼女の“Modern…” CROSS×BEATS Remix,GLAY,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 32,Master 56,Unlimited N/A,
|
||||
100824,miraie,未来へのプレリュード,カヒーナムジカ,Pick-Up (New + Revival),Easy N/A,Standard 21,Hard 35,Master 66,Unlimited N/A,
|
||||
100825,ranfes,狂乱セレブレーション,Yamajet,Pick-Up (New + Revival),Easy N/A,Standard 20,Hard 42,Master 65,Unlimited N/A,
|
||||
100826,nemure,眠れない歌,iru,Pick-Up (New + Revival),Easy N/A,Standard 15,Hard 38,Master 67,Unlimited 76,
|
||||
100827,yuwaku,誘惑,GLAY,Pick-Up (New + Revival),Easy N/A,Standard 15,Hard 26,Master 43,Unlimited N/A,
|
||||
100828,dontst,Don't Stop The Music feat.森高千里,tofubeats,Pick-Up (New + Revival),Easy N/A,Standard 15,Hard 32,Master 56,Unlimited 70,
|
||||
100829,mottai,もったいないとらんど,きゃりーぱみゅぱみゅ,Pick-Up (New + Revival),Easy N/A,Standard 10,Hard 26,Master 36,Unlimited N/A,
|
||||
100830,slysly,SLY,RIP SLYME,Pick-Up (New + Revival),Easy N/A,Standard 10,Hard 33,Master 58,Unlimited N/A,
|
||||
100831,lookam,(Where's)THE SILENT MAJORITY?,高橋優,Pick-Up (New + Revival),Easy N/A,Standard 17,Hard 34,Master 67,Unlimited N/A,
|
||||
100832,feverr,フィーバー,パスピエ,Pick-Up (New + Revival),Easy N/A,Standard 28,Hard 48,Master 68,Unlimited N/A,
|
||||
100833,fashio,ファッションモンスター,きゃりーぱみゅぱみゅ,Pick-Up (New + Revival),Easy N/A,Standard 8,Hard 24,Master 39,Unlimited N/A,
|
||||
100834,hagito,「ハギとこ!」のテーマ,ハギー,Pick-Up (New + Revival),Easy N/A,Standard 12,Hard 26,Master 50,Unlimited N/A,
|
||||
100835,invade,インベーダーインベーダー,きゃりーぱみゅぱみゅ,Pick-Up (New + Revival),Easy N/A,Standard 10,Hard 28,Master 47,Unlimited N/A,
|
||||
100836,ainoch,愛の地球祭,チームしゃちほこ,Pick-Up (New + Revival),Easy N/A,Standard 17,Hard 40,Master 59,Unlimited N/A,
|
||||
100837,nakama,仲間を探したい,神聖かまってちゃん,Pick-Up (New + Revival),Easy N/A,Standard 14,Hard 32,Master 53,Unlimited N/A,
|
||||
100838,ninjar,にんじゃりばんばん,きゃりーぱみゅぱみゅ,Pick-Up (New + Revival),Easy N/A,Standard 8,Hard 23,Master 41,Unlimited 65,
|
||||
100839,parall,パラレルスペック,ゲスの極み乙女。,Pick-Up (New + Revival),Easy N/A,Standard 14,Hard 35,Master 61,Unlimited N/A,
|
||||
100840,yukifu,雪降る夜にキスして,バンドじゃないもん!,Pick-Up (New + Revival),Easy N/A,Standard 13,Hard 29,Master 51,Unlimited N/A,
|
||||
100841,furiso,ふりそでーしょん,きゃりーぱみゅぱみゅ,Pick-Up (New + Revival),Easy N/A,Standard 12,Hard 24,Master 44,Unlimited 74,
|
||||
100842,honeyj,HONEY♡SUNRiSE ~jun Side~,jun with Aimee,Original,Easy 24,Standard 32,Hard 63,Master 88,Unlimited 93,
|
||||
100843,emeraj,EMERALD♡KISS ~jun Side~,jun with Aimee,Original,Easy 19,Standard 30,Hard 53,Master 85,Unlimited N/A,
|
||||
100844,dazzlo,DAZZLING♡SEASON (Original Side),jun,Original,Easy 16,Standard 35,Hard 60,Master 80,Unlimited 90,
|
||||
100844,shares,SHARE SONG,SHARE SONG,Original,Easy N/A,Standard N/A,Hard N/A,Master N/A,Unlimited N/A,
|
||||
|
@@ -1,8 +1,8 @@
|
||||
|
||||
from core.data import Data
|
||||
from core.config import CoreConfig
|
||||
from titles.cxb.schema import CxbProfileData, CxbScoreData, CxbItemData, CxbStaticData
|
||||
|
||||
|
||||
class CxbData(Data):
|
||||
def __init__(self, cfg: CoreConfig) -> None:
|
||||
super().__init__(cfg)
|
||||
|
||||
@@ -7,7 +7,8 @@ import re
|
||||
import inflection
|
||||
import logging, coloredlogs
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from typing import Dict
|
||||
from typing import Dict, Tuple
|
||||
from os import path
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.cxb.config import CxbConfig
|
||||
@@ -16,54 +17,93 @@ from titles.cxb.rev import CxbRev
|
||||
from titles.cxb.rss1 import CxbRevSunriseS1
|
||||
from titles.cxb.rss2 import CxbRevSunriseS2
|
||||
|
||||
|
||||
class CxbServlet(resource.Resource):
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
self.isLeaf = True
|
||||
self.cfg_dir = cfg_dir
|
||||
self.core_cfg = core_cfg
|
||||
self.game_cfg = CxbConfig()
|
||||
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/cxb.yaml")))
|
||||
if path.exists(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"):
|
||||
self.game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
self.logger = logging.getLogger("cxb")
|
||||
if not hasattr(self.logger, "inited"):
|
||||
log_fmt_str = "[%(asctime)s] CXB | %(levelname)s | %(message)s"
|
||||
log_fmt = logging.Formatter(log_fmt_str)
|
||||
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), encoding='utf8',
|
||||
when="d", backupCount=10)
|
||||
fileHandler = TimedRotatingFileHandler(
|
||||
"{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"),
|
||||
encoding="utf8",
|
||||
when="d",
|
||||
backupCount=10,
|
||||
)
|
||||
|
||||
fileHandler.setFormatter(log_fmt)
|
||||
|
||||
|
||||
consoleHandler = logging.StreamHandler()
|
||||
consoleHandler.setFormatter(log_fmt)
|
||||
|
||||
self.logger.addHandler(fileHandler)
|
||||
self.logger.addHandler(consoleHandler)
|
||||
|
||||
|
||||
self.logger.setLevel(self.game_cfg.server.loglevel)
|
||||
coloredlogs.install(level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str)
|
||||
coloredlogs.install(
|
||||
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||
)
|
||||
self.logger.inited = True
|
||||
|
||||
|
||||
self.versions = [
|
||||
CxbRev(core_cfg, self.game_cfg),
|
||||
CxbRevSunriseS1(core_cfg, self.game_cfg),
|
||||
CxbRevSunriseS2(core_cfg, self.game_cfg),
|
||||
]
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_allnet_info(
|
||||
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
|
||||
) -> Tuple[bool, str, str]:
|
||||
game_cfg = CxbConfig()
|
||||
if path.exists(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"):
|
||||
game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
if not game_cfg.server.enable:
|
||||
return (False, "", "")
|
||||
|
||||
if core_cfg.server.is_develop:
|
||||
return (
|
||||
True,
|
||||
f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/",
|
||||
"",
|
||||
)
|
||||
|
||||
return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v/", "")
|
||||
|
||||
def setup(self):
|
||||
if self.game_cfg.server.enable:
|
||||
endpoints.serverFromString(reactor, f"tcp:{self.game_cfg.server.port}:interface={self.core_cfg.server.listen_address}")\
|
||||
.listen(server.Site(CxbServlet(self.core_cfg, self.cfg_dir)))
|
||||
|
||||
if self.core_cfg.server.is_develop and self.game_cfg.server.ssl_enable:
|
||||
endpoints.serverFromString(reactor, f"ssl:{self.game_cfg.server.port_secure}"\
|
||||
f":interface={self.core_cfg.server.listen_address}:privateKey={self.game_cfg.server.ssl_key}:"\
|
||||
f"certKey={self.game_cfg.server.ssl_cert}")\
|
||||
.listen(server.Site(CxbServlet(self.core_cfg, self.cfg_dir)))
|
||||
endpoints.serverFromString(
|
||||
reactor,
|
||||
f"tcp:{self.game_cfg.server.port}:interface={self.core_cfg.server.listen_address}",
|
||||
).listen(server.Site(CxbServlet(self.core_cfg, self.cfg_dir)))
|
||||
|
||||
self.logger.info(f"Crossbeats title server ready on port {self.game_cfg.server.port} & {self.game_cfg.server.port_secure}")
|
||||
if self.core_cfg.server.is_develop and self.game_cfg.server.ssl_enable:
|
||||
endpoints.serverFromString(
|
||||
reactor,
|
||||
f"ssl:{self.game_cfg.server.port_secure}"
|
||||
f":interface={self.core_cfg.server.listen_address}:privateKey={self.game_cfg.server.ssl_key}:"
|
||||
f"certKey={self.game_cfg.server.ssl_cert}",
|
||||
).listen(server.Site(CxbServlet(self.core_cfg, self.cfg_dir)))
|
||||
|
||||
self.logger.info(
|
||||
f"Crossbeats title server ready on port {self.game_cfg.server.port} & {self.game_cfg.server.port_secure}"
|
||||
)
|
||||
else:
|
||||
self.logger.info(f"Crossbeats title server ready on port {self.game_cfg.server.port}")
|
||||
|
||||
self.logger.info(
|
||||
f"Crossbeats title server ready on port {self.game_cfg.server.port}"
|
||||
)
|
||||
|
||||
def render_POST(self, request: Request):
|
||||
version = 0
|
||||
@@ -80,21 +120,28 @@ class CxbServlet(resource.Resource):
|
||||
|
||||
except Exception as e:
|
||||
try:
|
||||
req_json: Dict = json.loads(req_bytes.decode().replace('"', '\\"').replace("'", '"'))
|
||||
req_json: Dict = json.loads(
|
||||
req_bytes.decode().replace('"', '\\"').replace("'", '"')
|
||||
)
|
||||
|
||||
except Exception as f:
|
||||
self.logger.warn(f"Error decoding json: {e} / {f} - {req_url} - {req_bytes}")
|
||||
self.logger.warn(
|
||||
f"Error decoding json: {e} / {f} - {req_url} - {req_bytes}"
|
||||
)
|
||||
return b""
|
||||
|
||||
|
||||
if req_json == {}:
|
||||
self.logger.warn(f"Empty json request to {req_url}")
|
||||
return b""
|
||||
|
||||
|
||||
cmd = url_split[len(url_split) - 1]
|
||||
subcmd = list(req_json.keys())[0]
|
||||
|
||||
if subcmd == "dldate":
|
||||
if not type(req_json["dldate"]) is dict or "filetype" not in req_json["dldate"]:
|
||||
if (
|
||||
not type(req_json["dldate"]) is dict
|
||||
or "filetype" not in req_json["dldate"]
|
||||
):
|
||||
self.logger.warn(f"Malformed dldate request: {req_url} {req_json}")
|
||||
return b""
|
||||
|
||||
@@ -103,7 +150,9 @@ class CxbServlet(resource.Resource):
|
||||
version = int(filetype_split[0])
|
||||
filetype_inflect_split = inflection.underscore(filetype).split("/")
|
||||
|
||||
match = re.match("^([A-Za-z]*)(\d\d\d\d)$", filetype_split[len(filetype_split) - 1])
|
||||
match = re.match(
|
||||
"^([A-Za-z]*)(\d\d\d\d)$", filetype_split[len(filetype_split) - 1]
|
||||
)
|
||||
if match:
|
||||
subcmd = f"{inflection.underscore(match.group(1))}xxxx"
|
||||
else:
|
||||
@@ -112,7 +161,7 @@ class CxbServlet(resource.Resource):
|
||||
filetype = subcmd
|
||||
|
||||
func_to_find = f"handle_{cmd}_{subcmd}_request"
|
||||
|
||||
|
||||
if version <= 10102:
|
||||
version_string = "Rev"
|
||||
internal_ver = CxbConstants.VER_CROSSBEATS_REV
|
||||
@@ -120,28 +169,28 @@ class CxbServlet(resource.Resource):
|
||||
elif version == 10113 or version == 10103:
|
||||
version_string = "Rev SunriseS1"
|
||||
internal_ver = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S1
|
||||
|
||||
|
||||
elif version >= 10114 or version == 10104:
|
||||
version_string = "Rev SunriseS2"
|
||||
internal_ver = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S2
|
||||
|
||||
|
||||
else:
|
||||
version_string = "Base"
|
||||
|
||||
|
||||
self.logger.info(f"{version_string} Request {req_url} -> {filetype}")
|
||||
self.logger.debug(req_json)
|
||||
|
||||
try:
|
||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||
resp = handler(req_json)
|
||||
|
||||
except AttributeError as e:
|
||||
|
||||
except AttributeError as e:
|
||||
self.logger.warning(f"Unhandled {version_string} request {req_url} - {e}")
|
||||
resp = {}
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error handling {version_string} method {req_url} - {e}")
|
||||
raise
|
||||
|
||||
self.logger.debug(f"{version_string} Response {resp}")
|
||||
|
||||
self.logger.debug(f"{version_string} Response {resp}")
|
||||
return json.dumps(resp, ensure_ascii=False).encode("utf-8")
|
||||
|
||||
@@ -8,13 +8,23 @@ from core.config import CoreConfig
|
||||
from titles.cxb.database import CxbData
|
||||
from titles.cxb.const import CxbConstants
|
||||
|
||||
|
||||
class CxbReader(BaseReader):
|
||||
def __init__(self, config: CoreConfig, version: int, bin_arg: Optional[str], opt_arg: Optional[str], extra: Optional[str]) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
config: CoreConfig,
|
||||
version: int,
|
||||
bin_arg: Optional[str],
|
||||
opt_arg: Optional[str],
|
||||
extra: Optional[str],
|
||||
) -> None:
|
||||
super().__init__(config, version, bin_arg, opt_arg, extra)
|
||||
self.data = CxbData(config)
|
||||
|
||||
try:
|
||||
self.logger.info(f"Start importer for {CxbConstants.game_ver_to_string(version)}")
|
||||
self.logger.info(
|
||||
f"Start importer for {CxbConstants.game_ver_to_string(version)}"
|
||||
)
|
||||
except IndexError:
|
||||
self.logger.error(f"Invalid project cxb version {version}")
|
||||
exit(1)
|
||||
@@ -28,7 +38,7 @@ class CxbReader(BaseReader):
|
||||
|
||||
if pull_bin_ram:
|
||||
self.read_csv(f"{self.bin_dir}")
|
||||
|
||||
|
||||
def read_csv(self, bin_dir: str) -> None:
|
||||
self.logger.info(f"Read csv from {bin_dir}")
|
||||
|
||||
@@ -45,18 +55,73 @@ class CxbReader(BaseReader):
|
||||
|
||||
if not "N/A" in row["standard"]:
|
||||
self.logger.info(f"Added song {song_id} chart 0")
|
||||
self.data.static.put_music(self.version, song_id, index, 0, title, artist, genre, int(row["standard"].replace("Standard ","").replace("N/A","0")))
|
||||
self.data.static.put_music(
|
||||
self.version,
|
||||
song_id,
|
||||
index,
|
||||
0,
|
||||
title,
|
||||
artist,
|
||||
genre,
|
||||
int(
|
||||
row["standard"]
|
||||
.replace("Standard ", "")
|
||||
.replace("N/A", "0")
|
||||
),
|
||||
)
|
||||
if not "N/A" in row["hard"]:
|
||||
self.logger.info(f"Added song {song_id} chart 1")
|
||||
self.data.static.put_music(self.version, song_id, index, 1, title, artist, genre, int(row["hard"].replace("Hard ","").replace("N/A","0")))
|
||||
self.data.static.put_music(
|
||||
self.version,
|
||||
song_id,
|
||||
index,
|
||||
1,
|
||||
title,
|
||||
artist,
|
||||
genre,
|
||||
int(row["hard"].replace("Hard ", "").replace("N/A", "0")),
|
||||
)
|
||||
if not "N/A" in row["master"]:
|
||||
self.logger.info(f"Added song {song_id} chart 2")
|
||||
self.data.static.put_music(self.version, song_id, index, 2, title, artist, genre, int(row["master"].replace("Master ","").replace("N/A","0")))
|
||||
self.data.static.put_music(
|
||||
self.version,
|
||||
song_id,
|
||||
index,
|
||||
2,
|
||||
title,
|
||||
artist,
|
||||
genre,
|
||||
int(
|
||||
row["master"].replace("Master ", "").replace("N/A", "0")
|
||||
),
|
||||
)
|
||||
if not "N/A" in row["unlimited"]:
|
||||
self.logger.info(f"Added song {song_id} chart 3")
|
||||
self.data.static.put_music(self.version, song_id, index, 3, title, artist, genre, int(row["unlimited"].replace("Unlimited ","").replace("N/A","0")))
|
||||
self.data.static.put_music(
|
||||
self.version,
|
||||
song_id,
|
||||
index,
|
||||
3,
|
||||
title,
|
||||
artist,
|
||||
genre,
|
||||
int(
|
||||
row["unlimited"]
|
||||
.replace("Unlimited ", "")
|
||||
.replace("N/A", "0")
|
||||
),
|
||||
)
|
||||
if not "N/A" in row["easy"]:
|
||||
self.logger.info(f"Added song {song_id} chart 4")
|
||||
self.data.static.put_music(self.version, song_id, index, 4, title, artist, genre, int(row["easy"].replace("Easy ","").replace("N/A","0")))
|
||||
self.data.static.put_music(
|
||||
self.version,
|
||||
song_id,
|
||||
index,
|
||||
4,
|
||||
title,
|
||||
artist,
|
||||
genre,
|
||||
int(row["easy"].replace("Easy ", "").replace("N/A", "0")),
|
||||
)
|
||||
except:
|
||||
self.logger.warn(f"Couldn't read csv file in {self.bin_dir}, skipping")
|
||||
|
||||
@@ -11,155 +11,191 @@ from titles.cxb.config import CxbConfig
|
||||
from titles.cxb.base import CxbBase
|
||||
from titles.cxb.const import CxbConstants
|
||||
|
||||
|
||||
class CxbRev(CxbBase):
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: CxbConfig) -> None:
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = CxbConstants.VER_CROSSBEATS_REV
|
||||
|
||||
|
||||
def handle_data_path_list_request(self, data: Dict) -> Dict:
|
||||
return { "data": "" }
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_putlog_request(self, data: Dict) -> Dict:
|
||||
if data["putlog"]["type"] == "ResultLog":
|
||||
score_data = json.loads(data["putlog"]["data"])
|
||||
userid = score_data['usid']
|
||||
userid = score_data["usid"]
|
||||
|
||||
self.data.score.put_playlog(userid, score_data['mcode'], score_data['difficulty'], score_data["score"], int(Decimal(score_data["clearrate"]) * 100), score_data["flawless"], score_data["super"], score_data["cool"], score_data["fast"], score_data["fast2"], score_data["slow"], score_data["slow2"], score_data["fail"], score_data["combo"])
|
||||
return({"data":True})
|
||||
return {"data": True }
|
||||
self.data.score.put_playlog(
|
||||
userid,
|
||||
score_data["mcode"],
|
||||
score_data["difficulty"],
|
||||
score_data["score"],
|
||||
int(Decimal(score_data["clearrate"]) * 100),
|
||||
score_data["flawless"],
|
||||
score_data["super"],
|
||||
score_data["cool"],
|
||||
score_data["fast"],
|
||||
score_data["fast2"],
|
||||
score_data["slow"],
|
||||
score_data["slow2"],
|
||||
score_data["fail"],
|
||||
score_data["combo"],
|
||||
)
|
||||
return {"data": True}
|
||||
return {"data": True}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_music_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rev_data/MusicArchiveList.csv") as music:
|
||||
lines = music.readlines()
|
||||
for line in lines:
|
||||
line_split = line.split(',')
|
||||
line_split = line.split(",")
|
||||
ret_str += f"{line_split[0]},{line_split[1]},{line_split[2]},{line_split[3]},{line_split[4]},{line_split[5]},{line_split[6]},{line_split[7]},{line_split[8]},{line_split[9]},{line_split[10]},{line_split[11]},{line_split[12]},{line_split[13]},{line_split[14]},\r\n"
|
||||
|
||||
return({"data":ret_str})
|
||||
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_item_list_icon_request(self, data: Dict) -> Dict:
|
||||
ret_str = "\r\n#ItemListIcon\r\n"
|
||||
with open(r"titles/cxb/rev_data/Item/ItemArchiveList_Icon.csv", encoding="utf-8") as item:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Item/ItemArchiveList_Icon.csv", encoding="utf-8"
|
||||
) as item:
|
||||
lines = item.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_item_list_skin_notes_request(self, data: Dict) -> Dict:
|
||||
ret_str = "\r\n#ItemListSkinNotes\r\n"
|
||||
with open(r"titles/cxb/rev_data/Item/ItemArchiveList_SkinNotes.csv", encoding="utf-8") as item:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Item/ItemArchiveList_SkinNotes.csv", encoding="utf-8"
|
||||
) as item:
|
||||
lines = item.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_item_list_skin_effect_request(self, data: Dict) -> Dict:
|
||||
ret_str = "\r\n#ItemListSkinEffect\r\n"
|
||||
with open(r"titles/cxb/rev_data/Item/ItemArchiveList_SkinEffect.csv", encoding="utf-8") as item:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Item/ItemArchiveList_SkinEffect.csv", encoding="utf-8"
|
||||
) as item:
|
||||
lines = item.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_item_list_skin_bg_request(self, data: Dict) -> Dict:
|
||||
ret_str = "\r\n#ItemListSkinBg\r\n"
|
||||
with open(r"titles/cxb/rev_data/Item/ItemArchiveList_SkinBg.csv", encoding="utf-8") as item:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Item/ItemArchiveList_SkinBg.csv", encoding="utf-8"
|
||||
) as item:
|
||||
lines = item.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_item_list_title_request(self, data: Dict) -> Dict:
|
||||
ret_str = "\r\n#ItemListTitle\r\n"
|
||||
with open(r"titles/cxb/rev_data/Item/ItemList_Title.csv", encoding="shift-jis") as item:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Item/ItemList_Title.csv", encoding="shift-jis"
|
||||
) as item:
|
||||
lines = item.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_shop_list_music_request(self, data: Dict) -> Dict:
|
||||
ret_str = "\r\n#ShopListMusic\r\n"
|
||||
with open(r"titles/cxb/rev_data/Shop/ShopList_Music.csv", encoding="shift-jis") as shop:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Shop/ShopList_Music.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_shop_list_icon_request(self, data: Dict) -> Dict:
|
||||
ret_str = "\r\n#ShopListIcon\r\n"
|
||||
with open(r"titles/cxb/rev_data/Shop/ShopList_Icon.csv", encoding="shift-jis") as shop:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Shop/ShopList_Icon.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_shop_list_title_request(self, data: Dict) -> Dict:
|
||||
ret_str = "\r\n#ShopListTitle\r\n"
|
||||
with open(r"titles/cxb/rev_data/Shop/ShopList_Title.csv", encoding="shift-jis") as shop:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Shop/ShopList_Title.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
|
||||
def handle_data_shop_list_skin_hud_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_shop_list_skin_arrow_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_shop_list_skin_hit_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_shop_list_skin_hud_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_shop_list_skin_arrow_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_shop_list_skin_hit_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_shop_list_sale_request(self, data: Dict) -> Dict:
|
||||
ret_str = "\r\n#ShopListSale\r\n"
|
||||
with open(r"titles/cxb/rev_data/Shop/ShopList_Sale.csv", encoding="shift-jis") as shop:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Shop/ShopList_Sale.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_exxxxx_request(self, data: Dict) -> Dict:
|
||||
extra_num = int(data["dldate"]["filetype"][-4:])
|
||||
ret_str = ""
|
||||
with open(fr"titles/cxb/rev_data/Ex000{extra_num}.csv", encoding="shift-jis") as stage:
|
||||
with open(
|
||||
rf"titles/cxb/rev_data/Ex000{extra_num}.csv", encoding="shift-jis"
|
||||
) as stage:
|
||||
lines = stage.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
|
||||
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
|
||||
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_news_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rev_data/NewsList.csv", encoding="UTF-8") as news:
|
||||
lines = news.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_tips_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_license_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
@@ -167,90 +203,104 @@ class CxbRev(CxbBase):
|
||||
lines = lic.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_course_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rev_data/Course/CourseList.csv", encoding="UTF-8") as course:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Course/CourseList.csv", encoding="UTF-8"
|
||||
) as course:
|
||||
lines = course.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_csxxxx_request(self, data: Dict) -> Dict:
|
||||
# Removed the CSVs since the format isnt quite right
|
||||
extra_num = int(data["dldate"]["filetype"][-4:])
|
||||
ret_str = ""
|
||||
with open(fr"titles/cxb/rev_data/Course/Cs000{extra_num}.csv", encoding="shift-jis") as course:
|
||||
with open(
|
||||
rf"titles/cxb/rev_data/Course/Cs000{extra_num}.csv", encoding="shift-jis"
|
||||
) as course:
|
||||
lines = course.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_mission_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rev_data/MissionList.csv", encoding="shift-jis") as mission:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/MissionList.csv", encoding="shift-jis"
|
||||
) as mission:
|
||||
lines = mission.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
|
||||
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
|
||||
def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_event_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rev_data/Event/EventArchiveList.csv", encoding="shift-jis") as mission:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Event/EventArchiveList.csv", encoding="shift-jis"
|
||||
) as mission:
|
||||
lines = mission.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_event_music_list_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
|
||||
def handle_data_event_mission_list_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
|
||||
def handle_data_event_achievement_single_high_score_list_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
|
||||
def handle_data_event_achievement_single_accumulation_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
|
||||
def handle_data_event_ranking_high_score_list_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
|
||||
def handle_data_event_ranking_accumulation_list_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
|
||||
def handle_data_event_ranking_stamp_list_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
|
||||
def handle_data_event_ranking_store_list_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
|
||||
def handle_data_event_ranking_area_list_request(self, data: Dict) -> Dict:
|
||||
return({"data": ""})
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_event_mission_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_event_achievement_single_high_score_list_request(
|
||||
self, data: Dict
|
||||
) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_event_achievement_single_accumulation_request(
|
||||
self, data: Dict
|
||||
) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_event_ranking_high_score_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_event_ranking_accumulation_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_event_ranking_stamp_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_event_ranking_store_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_event_ranking_area_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rev_data/Event/EventStampList.csv", encoding="shift-jis") as event:
|
||||
with open(
|
||||
r"titles/cxb/rev_data/Event/EventStampList.csv", encoding="shift-jis"
|
||||
) as event:
|
||||
lines = event.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
|
||||
return({"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"})
|
||||
|
||||
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
||||
|
||||
def handle_data_server_state_request(self, data: Dict) -> Dict:
|
||||
return({"data": True})
|
||||
return {"data": True}
|
||||
|
||||
@@ -11,128 +11,147 @@ from titles.cxb.config import CxbConfig
|
||||
from titles.cxb.base import CxbBase
|
||||
from titles.cxb.const import CxbConstants
|
||||
|
||||
|
||||
class CxbRevSunriseS1(CxbBase):
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: CxbConfig) -> None:
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S1
|
||||
|
||||
def handle_data_path_list_request(self, data: Dict) -> Dict:
|
||||
return { "data": "" }
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_path_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_music_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rss1_data/MusicArchiveList.csv") as music:
|
||||
lines = music.readlines()
|
||||
for line in lines:
|
||||
line_split = line.split(',')
|
||||
line_split = line.split(",")
|
||||
ret_str += f"{line_split[0]},{line_split[1]},{line_split[2]},{line_split[3]},{line_split[4]},{line_split[5]},{line_split[6]},{line_split[7]},{line_split[8]},{line_split[9]},{line_split[10]},{line_split[11]},{line_split[12]},{line_split[13]},{line_split[14]},\r\n"
|
||||
|
||||
return({"data":ret_str})
|
||||
|
||||
@cached(lifetime=86400)
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_item_list_detail_request(self, data: Dict) -> Dict:
|
||||
#ItemListIcon load
|
||||
# ItemListIcon load
|
||||
ret_str = "#ItemListIcon\r\n"
|
||||
with open(r"titles/cxb/rss1_data/Item/ItemList_Icon.csv", encoding="shift-jis") as item:
|
||||
with open(
|
||||
r"titles/cxb/rss1_data/Item/ItemList_Icon.csv", encoding="shift-jis"
|
||||
) as item:
|
||||
lines = item.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ItemListTitle load
|
||||
# ItemListTitle load
|
||||
ret_str += "\r\n#ItemListTitle\r\n"
|
||||
with open(r"titles/cxb/rss1_data/Item/ItemList_Title.csv", encoding="shift-jis") as item:
|
||||
with open(
|
||||
r"titles/cxb/rss1_data/Item/ItemList_Title.csv", encoding="shift-jis"
|
||||
) as item:
|
||||
lines = item.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_shop_list_detail_request(self, data: Dict) -> Dict:
|
||||
#ShopListIcon load
|
||||
# ShopListIcon load
|
||||
ret_str = "#ShopListIcon\r\n"
|
||||
with open(r"titles/cxb/rss1_data/Shop/ShopList_Icon.csv", encoding="utf-8") as shop:
|
||||
with open(
|
||||
r"titles/cxb/rss1_data/Shop/ShopList_Icon.csv", encoding="utf-8"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListMusic load
|
||||
ret_str += "\r\n#ShopListMusic\r\n"
|
||||
with open(r"titles/cxb/rss1_data/Shop/ShopList_Music.csv", encoding="utf-8") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListSale load
|
||||
ret_str += "\r\n#ShopListSale\r\n"
|
||||
with open(r"titles/cxb/rss1_data/Shop/ShopList_Sale.csv", encoding="shift-jis") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListSkinBg load
|
||||
ret_str += "\r\n#ShopListSkinBg\r\n"
|
||||
with open(r"titles/cxb/rss1_data/Shop/ShopList_SkinBg.csv", encoding="shift-jis") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListSkinEffect load
|
||||
ret_str += "\r\n#ShopListSkinEffect\r\n"
|
||||
with open(r"titles/cxb/rss1_data/Shop/ShopList_SkinEffect.csv", encoding="shift-jis") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListSkinNotes load
|
||||
ret_str += "\r\n#ShopListSkinNotes\r\n"
|
||||
with open(r"titles/cxb/rss1_data/Shop/ShopList_SkinNotes.csv", encoding="shift-jis") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListTitle load
|
||||
ret_str += "\r\n#ShopListTitle\r\n"
|
||||
with open(r"titles/cxb/rss1_data/Shop/ShopList_Title.csv", encoding="utf-8") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
|
||||
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_ex0001_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_oe0001_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
@cached(lifetime=86400)
|
||||
# ShopListMusic load
|
||||
ret_str += "\r\n#ShopListMusic\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss1_data/Shop/ShopList_Music.csv", encoding="utf-8"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
# ShopListSale load
|
||||
ret_str += "\r\n#ShopListSale\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss1_data/Shop/ShopList_Sale.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
# ShopListSkinBg load
|
||||
ret_str += "\r\n#ShopListSkinBg\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss1_data/Shop/ShopList_SkinBg.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
# ShopListSkinEffect load
|
||||
ret_str += "\r\n#ShopListSkinEffect\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss1_data/Shop/ShopList_SkinEffect.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
# ShopListSkinNotes load
|
||||
ret_str += "\r\n#ShopListSkinNotes\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss1_data/Shop/ShopList_SkinNotes.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
# ShopListTitle load
|
||||
ret_str += "\r\n#ShopListTitle\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss1_data/Shop/ShopList_Title.csv", encoding="utf-8"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_ex0001_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_oe0001_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_news_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rss1_data/NewsList.csv", encoding="UTF-8") as news:
|
||||
lines = news.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_tips_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_release_info_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_random_music_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
@@ -141,10 +160,12 @@ class CxbRevSunriseS1(CxbBase):
|
||||
count = 0
|
||||
for line in lines:
|
||||
line_split = line.split(",")
|
||||
ret_str += str(count) + "," + line_split[0] + "," + line_split[0] + ",\r\n"
|
||||
ret_str += (
|
||||
str(count) + "," + line_split[0] + "," + line_split[0] + ",\r\n"
|
||||
)
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
return({"data":ret_str})
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_license_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
@@ -152,54 +173,58 @@ class CxbRevSunriseS1(CxbBase):
|
||||
lines = licenses.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_course_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rss1_data/Course/CourseList.csv", encoding="UTF-8") as course:
|
||||
with open(
|
||||
r"titles/cxb/rss1_data/Course/CourseList.csv", encoding="UTF-8"
|
||||
) as course:
|
||||
lines = course.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_csxxxx_request(self, data: Dict) -> Dict:
|
||||
extra_num = int(data["dldate"]["filetype"][-4:])
|
||||
ret_str = ""
|
||||
with open(fr"titles/cxb/rss1_data/Course/Cs{extra_num}.csv", encoding="shift-jis") as course:
|
||||
with open(
|
||||
rf"titles/cxb/rss1_data/Course/Cs{extra_num}.csv", encoding="shift-jis"
|
||||
) as course:
|
||||
lines = course.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_mission_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_partner_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
# Lord forgive me for the sins I am about to commit
|
||||
for i in range(0,10):
|
||||
for i in range(0, 10):
|
||||
ret_str += f"80000{i},{i},{i},0,10000,,\r\n"
|
||||
ret_str += f"80000{i},{i},{i},1,10500,,\r\n"
|
||||
ret_str += f"80000{i},{i},{i},2,10500,,\r\n"
|
||||
for i in range(10,13):
|
||||
for i in range(10, 13):
|
||||
ret_str += f"8000{i},{i},{i},0,10000,,\r\n"
|
||||
ret_str += f"8000{i},{i},{i},1,10500,,\r\n"
|
||||
ret_str += f"8000{i},{i},{i},2,10500,,\r\n"
|
||||
ret_str +="\r\n---\r\n0,150,100,100,100,100,\r\n"
|
||||
for i in range(1,130):
|
||||
ret_str +=f"{i},100,100,100,100,100,\r\n"
|
||||
|
||||
ret_str += "\r\n---\r\n0,150,100,100,100,100,\r\n"
|
||||
for i in range(1, 130):
|
||||
ret_str += f"{i},100,100,100,100,100,\r\n"
|
||||
|
||||
ret_str += "---\r\n"
|
||||
return({"data": ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_partnerxxxx_request(self, data: Dict) -> Dict:
|
||||
partner_num = int(data["dldate"]["filetype"][-4:])
|
||||
@@ -208,50 +233,54 @@ class CxbRevSunriseS1(CxbBase):
|
||||
lines = partner.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data": ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_server_state_request(self, data: Dict) -> Dict:
|
||||
return({"data": True})
|
||||
|
||||
return {"data": True}
|
||||
|
||||
def handle_data_settings_request(self, data: Dict) -> Dict:
|
||||
return({"data": "2,\r\n"})
|
||||
return {"data": "2,\r\n"}
|
||||
|
||||
def handle_data_story_list_request(self, data: Dict) -> Dict:
|
||||
#story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu
|
||||
# story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu
|
||||
ret_str = "\r\n"
|
||||
ret_str += f"st0000,RISING PURPLE,10104,1464370990,4096483201,Cs1000,-1,purple,\r\n"
|
||||
ret_str += f"st0001,REBEL YELL,10104,1467999790,4096483201,Cs1000,-1,chaset,\r\n"
|
||||
ret_str += (
|
||||
f"st0000,RISING PURPLE,10104,1464370990,4096483201,Cs1000,-1,purple,\r\n"
|
||||
)
|
||||
ret_str += (
|
||||
f"st0001,REBEL YELL,10104,1467999790,4096483201,Cs1000,-1,chaset,\r\n"
|
||||
)
|
||||
ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n"
|
||||
return({"data": ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_stxxxx_request(self, data: Dict) -> Dict:
|
||||
story_num = int(data["dldate"]["filetype"][-4:])
|
||||
ret_str = ""
|
||||
for i in range(1,11):
|
||||
ret_str +=f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n"
|
||||
return({"data": ret_str})
|
||||
for i in range(1, 11):
|
||||
ret_str += f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n"
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":"Cs1032,1,1,1,1,1,1,1,1,1,1,\r\n"})
|
||||
return {"data": "Cs1032,1,1,1,1,1,1,1,1,1,1,\r\n"}
|
||||
|
||||
def handle_data_premium_list_request(self, data: Dict) -> Dict:
|
||||
return({"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"})
|
||||
return {"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"}
|
||||
|
||||
def handle_data_event_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_event_detail_list_request(self, data: Dict) -> Dict:
|
||||
event_id = data["dldate"]["filetype"].split("/")[2]
|
||||
if "EventStampMapListCs1002" in event_id:
|
||||
return({"data":"1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"})
|
||||
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
||||
elif "EventStampList" in event_id:
|
||||
return({"data":"Cs1002,1,1,1,1,1,1,1,1,1,1,\r\n"})
|
||||
return {"data": "Cs1002,1,1,1,1,1,1,1,1,1,1,\r\n"}
|
||||
else:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
|
||||
event_id = data["dldate"]["filetype"].split("/")[2]
|
||||
if "EventStampMapListCs1002" in event_id:
|
||||
return({"data":"1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"})
|
||||
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
||||
else:
|
||||
return({"data":""})
|
||||
return {"data": ""}
|
||||
|
||||
@@ -11,128 +11,147 @@ from titles.cxb.config import CxbConfig
|
||||
from titles.cxb.base import CxbBase
|
||||
from titles.cxb.const import CxbConstants
|
||||
|
||||
|
||||
class CxbRevSunriseS2(CxbBase):
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: CxbConfig) -> None:
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S2_OMNI
|
||||
|
||||
def handle_data_path_list_request(self, data: Dict) -> Dict:
|
||||
return { "data": "" }
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_path_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_music_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rss2_data/MusicArchiveList.csv") as music:
|
||||
lines = music.readlines()
|
||||
for line in lines:
|
||||
line_split = line.split(',')
|
||||
line_split = line.split(",")
|
||||
ret_str += f"{line_split[0]},{line_split[1]},{line_split[2]},{line_split[3]},{line_split[4]},{line_split[5]},{line_split[6]},{line_split[7]},{line_split[8]},{line_split[9]},{line_split[10]},{line_split[11]},{line_split[12]},{line_split[13]},{line_split[14]},\r\n"
|
||||
|
||||
return({"data":ret_str})
|
||||
|
||||
@cached(lifetime=86400)
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_item_list_detail_request(self, data: Dict) -> Dict:
|
||||
#ItemListIcon load
|
||||
# ItemListIcon load
|
||||
ret_str = "#ItemListIcon\r\n"
|
||||
with open(r"titles/cxb/rss2_data/Item/ItemList_Icon.csv", encoding="utf-8") as item:
|
||||
with open(
|
||||
r"titles/cxb/rss2_data/Item/ItemList_Icon.csv", encoding="utf-8"
|
||||
) as item:
|
||||
lines = item.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ItemListTitle load
|
||||
|
||||
# ItemListTitle load
|
||||
ret_str += "\r\n#ItemListTitle\r\n"
|
||||
with open(r"titles/cxb/rss2_data/Item/ItemList_Title.csv", encoding="utf-8") as item:
|
||||
with open(
|
||||
r"titles/cxb/rss2_data/Item/ItemList_Title.csv", encoding="utf-8"
|
||||
) as item:
|
||||
lines = item.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_shop_list_detail_request(self, data: Dict) -> Dict:
|
||||
#ShopListIcon load
|
||||
# ShopListIcon load
|
||||
ret_str = "#ShopListIcon\r\n"
|
||||
with open(r"titles/cxb/rss2_data/Shop/ShopList_Icon.csv", encoding="utf-8") as shop:
|
||||
with open(
|
||||
r"titles/cxb/rss2_data/Shop/ShopList_Icon.csv", encoding="utf-8"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListMusic load
|
||||
ret_str += "\r\n#ShopListMusic\r\n"
|
||||
with open(r"titles/cxb/rss2_data/Shop/ShopList_Music.csv", encoding="utf-8") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListSale load
|
||||
ret_str += "\r\n#ShopListSale\r\n"
|
||||
with open(r"titles/cxb/rss2_data/Shop/ShopList_Sale.csv", encoding="shift-jis") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListSkinBg load
|
||||
ret_str += "\r\n#ShopListSkinBg\r\n"
|
||||
with open(r"titles/cxb/rss2_data/Shop/ShopList_SkinBg.csv", encoding="shift-jis") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListSkinEffect load
|
||||
ret_str += "\r\n#ShopListSkinEffect\r\n"
|
||||
with open(r"titles/cxb/rss2_data/Shop/ShopList_SkinEffect.csv", encoding="shift-jis") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListSkinNotes load
|
||||
ret_str += "\r\n#ShopListSkinNotes\r\n"
|
||||
with open(r"titles/cxb/rss2_data/Shop/ShopList_SkinNotes.csv", encoding="shift-jis") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
#ShopListTitle load
|
||||
ret_str += "\r\n#ShopListTitle\r\n"
|
||||
with open(r"titles/cxb/rss2_data/Shop/ShopList_Title.csv", encoding="utf-8") as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
|
||||
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_ex0001_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_oe0001_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
@cached(lifetime=86400)
|
||||
# ShopListMusic load
|
||||
ret_str += "\r\n#ShopListMusic\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss2_data/Shop/ShopList_Music.csv", encoding="utf-8"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
# ShopListSale load
|
||||
ret_str += "\r\n#ShopListSale\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss2_data/Shop/ShopList_Sale.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
# ShopListSkinBg load
|
||||
ret_str += "\r\n#ShopListSkinBg\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss2_data/Shop/ShopList_SkinBg.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
# ShopListSkinEffect load
|
||||
ret_str += "\r\n#ShopListSkinEffect\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss2_data/Shop/ShopList_SkinEffect.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
# ShopListSkinNotes load
|
||||
ret_str += "\r\n#ShopListSkinNotes\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss2_data/Shop/ShopList_SkinNotes.csv", encoding="shift-jis"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
|
||||
# ShopListTitle load
|
||||
ret_str += "\r\n#ShopListTitle\r\n"
|
||||
with open(
|
||||
r"titles/cxb/rss2_data/Shop/ShopList_Title.csv", encoding="utf-8"
|
||||
) as shop:
|
||||
lines = shop.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_ex0001_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_oe0001_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_news_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rss2_data/NewsList.csv", encoding="UTF-8") as news:
|
||||
lines = news.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_tips_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_release_info_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_random_music_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
@@ -141,10 +160,12 @@ class CxbRevSunriseS2(CxbBase):
|
||||
count = 0
|
||||
for line in lines:
|
||||
line_split = line.split(",")
|
||||
ret_str += str(count) + "," + line_split[0] + "," + line_split[0] + ",\r\n"
|
||||
ret_str += (
|
||||
str(count) + "," + line_split[0] + "," + line_split[0] + ",\r\n"
|
||||
)
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
return({"data":ret_str})
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_license_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
@@ -152,54 +173,58 @@ class CxbRevSunriseS2(CxbBase):
|
||||
lines = licenses.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_course_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/rss2_data/Course/CourseList.csv", encoding="UTF-8") as course:
|
||||
with open(
|
||||
r"titles/cxb/rss2_data/Course/CourseList.csv", encoding="UTF-8"
|
||||
) as course:
|
||||
lines = course.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_csxxxx_request(self, data: Dict) -> Dict:
|
||||
extra_num = int(data["dldate"]["filetype"][-4:])
|
||||
ret_str = ""
|
||||
with open(fr"titles/cxb/rss2_data/Course/Cs{extra_num}.csv", encoding="shift-jis") as course:
|
||||
with open(
|
||||
rf"titles/cxb/rss2_data/Course/Cs{extra_num}.csv", encoding="shift-jis"
|
||||
) as course:
|
||||
lines = course.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data":ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_mission_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_partner_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
# Lord forgive me for the sins I am about to commit
|
||||
for i in range(0,10):
|
||||
for i in range(0, 10):
|
||||
ret_str += f"80000{i},{i},{i},0,10000,,\r\n"
|
||||
ret_str += f"80000{i},{i},{i},1,10500,,\r\n"
|
||||
ret_str += f"80000{i},{i},{i},2,10500,,\r\n"
|
||||
for i in range(10,13):
|
||||
for i in range(10, 13):
|
||||
ret_str += f"8000{i},{i},{i},0,10000,,\r\n"
|
||||
ret_str += f"8000{i},{i},{i},1,10500,,\r\n"
|
||||
ret_str += f"8000{i},{i},{i},2,10500,,\r\n"
|
||||
ret_str +="\r\n---\r\n0,150,100,100,100,100,\r\n"
|
||||
for i in range(1,130):
|
||||
ret_str +=f"{i},100,100,100,100,100,\r\n"
|
||||
|
||||
ret_str += "\r\n---\r\n0,150,100,100,100,100,\r\n"
|
||||
for i in range(1, 130):
|
||||
ret_str += f"{i},100,100,100,100,100,\r\n"
|
||||
|
||||
ret_str += "---\r\n"
|
||||
return({"data": ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_partnerxxxx_request(self, data: Dict) -> Dict:
|
||||
partner_num = int(data["dldate"]["filetype"][-4:])
|
||||
@@ -208,55 +233,65 @@ class CxbRevSunriseS2(CxbBase):
|
||||
lines = partner.readlines()
|
||||
for line in lines:
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
return({"data": ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_server_state_request(self, data: Dict) -> Dict:
|
||||
return({"data": True})
|
||||
|
||||
return {"data": True}
|
||||
|
||||
def handle_data_settings_request(self, data: Dict) -> Dict:
|
||||
return({"data": "2,\r\n"})
|
||||
return {"data": "2,\r\n"}
|
||||
|
||||
def handle_data_story_list_request(self, data: Dict) -> Dict:
|
||||
#story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu
|
||||
# story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu
|
||||
ret_str = "\r\n"
|
||||
ret_str += f"st0000,RISING PURPLE,10104,1464370990,4096483201,Cs1000,-1,purple,\r\n"
|
||||
ret_str += f"st0001,REBEL YELL,10104,1467999790,4096483201,Cs1000,-1,chaset,\r\n"
|
||||
ret_str += (
|
||||
f"st0000,RISING PURPLE,10104,1464370990,4096483201,Cs1000,-1,purple,\r\n"
|
||||
)
|
||||
ret_str += (
|
||||
f"st0001,REBEL YELL,10104,1467999790,4096483201,Cs1000,-1,chaset,\r\n"
|
||||
)
|
||||
ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n"
|
||||
return({"data": ret_str})
|
||||
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_stxxxx_request(self, data: Dict) -> Dict:
|
||||
story_num = int(data["dldate"]["filetype"][-4:])
|
||||
ret_str = ""
|
||||
# Each stories appears to have 10 pieces based on the wiki but as on how they are set.... no clue
|
||||
for i in range(1,11):
|
||||
ret_str +=f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n"
|
||||
return({"data": ret_str})
|
||||
for i in range(1, 11):
|
||||
ret_str += f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n"
|
||||
return {"data": ret_str}
|
||||
|
||||
def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":"Cs1002,1,1,1,1,1,1,1,1,1,1,\r\n"})
|
||||
return {"data": "Cs1002,1,1,1,1,1,1,1,1,1,1,\r\n"}
|
||||
|
||||
def handle_data_premium_list_request(self, data: Dict) -> Dict:
|
||||
return({"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"})
|
||||
return {"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"}
|
||||
|
||||
def handle_data_event_list_request(self, data: Dict) -> Dict:
|
||||
return({"data":"Cs4001,0,10000,1601510400,1604188799,1,nv2006,1,\r\nCs4005,0,10000,1609459200,1615766399,1,nv2006,1,\r\n"})
|
||||
return {
|
||||
"data": "Cs4001,0,10000,1601510400,1604188799,1,nv2006,1,\r\nCs4005,0,10000,1609459200,1615766399,1,nv2006,1,\r\n"
|
||||
}
|
||||
|
||||
def handle_data_event_detail_list_request(self, data: Dict) -> Dict:
|
||||
event_id = data["dldate"]["filetype"].split("/")[2]
|
||||
if "Cs4001" in event_id:
|
||||
return({"data":"#EventMusicList\r\n1,zonzon2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n2,moonki,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n3,tricko,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n"})
|
||||
return {
|
||||
"data": "#EventMusicList\r\n1,zonzon2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n2,moonki,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n3,tricko,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n"
|
||||
}
|
||||
elif "Cs4005" in event_id:
|
||||
return({"data":"#EventMusicList\r\n2,firstl,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n2,valent,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n2,dazzli2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n"})
|
||||
return {
|
||||
"data": "#EventMusicList\r\n2,firstl,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n2,valent,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n2,dazzli2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\r\n"
|
||||
}
|
||||
elif "EventStampMapListCs1002" in event_id:
|
||||
return({"data":"1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"})
|
||||
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
||||
elif "EventStampList" in event_id:
|
||||
return({"data":"Cs1002,1,1,1,1,1,1,1,1,1,1,\r\n"})
|
||||
return {"data": "Cs1002,1,1,1,1,1,1,1,1,1,1,\r\n"}
|
||||
else:
|
||||
return({"data":""})
|
||||
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
|
||||
event_id = data["dldate"]["filetype"].split("/")[2]
|
||||
if "EventStampMapListCs1002" in event_id:
|
||||
return({"data":"1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"})
|
||||
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
||||
else:
|
||||
return({"data":""})
|
||||
return {"data": ""}
|
||||
|
||||
@@ -14,32 +14,29 @@ energy = Table(
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade"), nullable=False),
|
||||
Column("energy", Integer, nullable=False, server_default="0"),
|
||||
UniqueConstraint("user", name="cxb_rev_energy_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
class CxbItemData(BaseData):
|
||||
def put_energy(self, user_id: int, rev_energy: int) -> Optional[int]:
|
||||
sql = insert(energy).values(
|
||||
user = user_id,
|
||||
energy = rev_energy
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
energy = rev_energy
|
||||
)
|
||||
class CxbItemData(BaseData):
|
||||
def put_energy(self, user_id: int, rev_energy: int) -> Optional[int]:
|
||||
sql = insert(energy).values(user=user_id, energy=rev_energy)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(energy=rev_energy)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.error(f"{__name__} failed to insert item! user: {user_id}, energy: {rev_energy}")
|
||||
self.logger.error(
|
||||
f"{__name__} failed to insert item! user: {user_id}, energy: {rev_energy}"
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_energy(self, user_id: int) -> Optional[Dict]:
|
||||
sql = energy.select(
|
||||
and_(energy.c.user == user_id)
|
||||
)
|
||||
sql = energy.select(and_(energy.c.user == user_id))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
@@ -16,57 +16,63 @@ profile = Table(
|
||||
Column("index", Integer, nullable=False),
|
||||
Column("data", JSON, nullable=False),
|
||||
UniqueConstraint("user", "index", name="cxb_profile_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class CxbProfileData(BaseData):
|
||||
def put_profile(self, user_id: int, version: int, index: int, data: JSON) -> Optional[int]:
|
||||
def put_profile(
|
||||
self, user_id: int, version: int, index: int, data: JSON
|
||||
) -> Optional[int]:
|
||||
sql = insert(profile).values(
|
||||
user = user_id,
|
||||
version = version,
|
||||
index = index,
|
||||
data = data
|
||||
user=user_id, version=version, index=index, data=data
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
index = index,
|
||||
data = data
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(index=index, data=data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.error(f"{__name__} failed to update! user: {user_id}, index: {index}, data: {data}")
|
||||
self.logger.error(
|
||||
f"{__name__} failed to update! user: {user_id}, index: {index}, data: {data}"
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
return result.lastrowid
|
||||
|
||||
def get_profile(self, aime_id: int, version: int) -> Optional[List[Dict]]:
|
||||
"""
|
||||
Given a game version and either a profile or aime id, return the profile
|
||||
"""
|
||||
sql = profile.select(and_(
|
||||
profile.c.version == version,
|
||||
profile.c.user == aime_id
|
||||
))
|
||||
|
||||
sql = profile.select(
|
||||
and_(profile.c.version == version, profile.c.user == aime_id)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_profile_index(self, index: int, aime_id: int = None, version: int = None) -> Optional[Dict]:
|
||||
def get_profile_index(
|
||||
self, index: int, aime_id: int = None, version: int = None
|
||||
) -> Optional[Dict]:
|
||||
"""
|
||||
Given a game version and either a profile or aime id, return the profile
|
||||
"""
|
||||
if aime_id is not None and version is not None and index is not None:
|
||||
sql = profile.select(and_(
|
||||
sql = profile.select(
|
||||
and_(
|
||||
profile.c.version == version,
|
||||
profile.c.user == aime_id,
|
||||
profile.c.index == index
|
||||
))
|
||||
profile.c.index == index,
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"get_profile: Bad arguments!! aime_id {aime_id} version {version}")
|
||||
self.logger.error(
|
||||
f"get_profile: Bad arguments!! aime_id {aime_id} version {version}"
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
@@ -18,7 +18,7 @@ score = Table(
|
||||
Column("song_index", Integer),
|
||||
Column("data", JSON),
|
||||
UniqueConstraint("user", "song_mcode", "song_index", name="cxb_score_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
playlog = Table(
|
||||
@@ -40,7 +40,7 @@ playlog = Table(
|
||||
Column("fail", Integer),
|
||||
Column("combo", Integer),
|
||||
Column("date_scored", TIMESTAMP, server_default=func.now()),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
ranking = Table(
|
||||
@@ -53,11 +53,19 @@ ranking = Table(
|
||||
Column("score", Integer),
|
||||
Column("clear", Integer),
|
||||
UniqueConstraint("user", "rev_id", name="cxb_ranking_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class CxbScoreData(BaseData):
|
||||
def put_best_score(self, user_id: int, song_mcode: str, game_version: int, song_index: int, data: JSON) -> Optional[int]:
|
||||
def put_best_score(
|
||||
self,
|
||||
user_id: int,
|
||||
song_mcode: str,
|
||||
game_version: int,
|
||||
song_index: int,
|
||||
data: JSON,
|
||||
) -> Optional[int]:
|
||||
"""
|
||||
Update the user's best score for a chart
|
||||
"""
|
||||
@@ -66,22 +74,37 @@ class CxbScoreData(BaseData):
|
||||
song_mcode=song_mcode,
|
||||
game_version=game_version,
|
||||
song_index=song_index,
|
||||
data=data
|
||||
data=data,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
data = sql.inserted.data
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(data=sql.inserted.data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.error(f"{__name__} failed to insert best score! profile: {user_id}, song: {song_mcode}, data: {data}")
|
||||
self.logger.error(
|
||||
f"{__name__} failed to insert best score! profile: {user_id}, song: {song_mcode}, data: {data}"
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
return result.lastrowid
|
||||
|
||||
def put_playlog(self, user_id: int, song_mcode: str, chart_id: int, score: int, clear: int, flawless: int, this_super: int,
|
||||
cool: int, this_fast: int, this_fast2: int, this_slow: int, this_slow2: int, fail: int, combo: int) -> Optional[int]:
|
||||
|
||||
def put_playlog(
|
||||
self,
|
||||
user_id: int,
|
||||
song_mcode: str,
|
||||
chart_id: int,
|
||||
score: int,
|
||||
clear: int,
|
||||
flawless: int,
|
||||
this_super: int,
|
||||
cool: int,
|
||||
this_fast: int,
|
||||
this_fast2: int,
|
||||
this_slow: int,
|
||||
this_slow2: int,
|
||||
fail: int,
|
||||
combo: int,
|
||||
) -> Optional[int]:
|
||||
"""
|
||||
Add an entry to the user's play log
|
||||
"""
|
||||
@@ -99,45 +122,42 @@ class CxbScoreData(BaseData):
|
||||
slow=this_slow,
|
||||
slow2=this_slow2,
|
||||
fail=fail,
|
||||
combo=combo
|
||||
combo=combo,
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
self.logger.error(f"{__name__} failed to insert playlog! profile: {user_id}, song: {song_mcode}, chart: {chart_id}")
|
||||
self.logger.error(
|
||||
f"{__name__} failed to insert playlog! profile: {user_id}, song: {song_mcode}, chart: {chart_id}"
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
return result.lastrowid
|
||||
|
||||
def put_ranking(self, user_id: int, rev_id: int, song_id: int, score: int, clear: int) -> Optional[int]:
|
||||
def put_ranking(
|
||||
self, user_id: int, rev_id: int, song_id: int, score: int, clear: int
|
||||
) -> Optional[int]:
|
||||
"""
|
||||
Add an entry to the user's ranking logs
|
||||
"""
|
||||
if song_id == 0:
|
||||
sql = insert(ranking).values(
|
||||
user=user_id,
|
||||
rev_id=rev_id,
|
||||
score=score,
|
||||
clear=clear
|
||||
user=user_id, rev_id=rev_id, score=score, clear=clear
|
||||
)
|
||||
else:
|
||||
sql = insert(ranking).values(
|
||||
user=user_id,
|
||||
rev_id=rev_id,
|
||||
song_id=song_id,
|
||||
score=score,
|
||||
clear=clear
|
||||
user=user_id, rev_id=rev_id, song_id=song_id, score=score, clear=clear
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
score = score
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(score=score)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.error(f"{__name__} failed to insert ranking log! profile: {user_id}, score: {score}, clear: {clear}")
|
||||
self.logger.error(
|
||||
f"{__name__} failed to insert ranking log! profile: {user_id}, score: {score}, clear: {clear}"
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
return result.lastrowid
|
||||
|
||||
def get_best_score(self, user_id: int, song_mcode: int) -> Optional[Dict]:
|
||||
@@ -146,21 +166,22 @@ class CxbScoreData(BaseData):
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_best_scores(self, user_id: int) -> Optional[Dict]:
|
||||
sql = score.select(score.c.user == user_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_best_rankings(self, user_id: int) -> Optional[List[Dict]]:
|
||||
sql = ranking.select(
|
||||
ranking.c.user == user_id
|
||||
)
|
||||
sql = ranking.select(ranking.c.user == user_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
@@ -21,54 +21,75 @@ music = Table(
|
||||
Column("artist", String(255)),
|
||||
Column("category", String(255)),
|
||||
Column("level", Float),
|
||||
UniqueConstraint("version", "songId", "chartId", "index", name="cxb_static_music_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
UniqueConstraint(
|
||||
"version", "songId", "chartId", "index", name="cxb_static_music_uk"
|
||||
),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class CxbStaticData(BaseData):
|
||||
def put_music(self, version: int, mcode: str, index: int, chart: int, title: str, artist: str, category: str, level: float ) -> Optional[int]:
|
||||
def put_music(
|
||||
self,
|
||||
version: int,
|
||||
mcode: str,
|
||||
index: int,
|
||||
chart: int,
|
||||
title: str,
|
||||
artist: str,
|
||||
category: str,
|
||||
level: float,
|
||||
) -> Optional[int]:
|
||||
sql = insert(music).values(
|
||||
version = version,
|
||||
songId = mcode,
|
||||
index = index,
|
||||
chartId = chart,
|
||||
title = title,
|
||||
artist = artist,
|
||||
category = category,
|
||||
level = level
|
||||
version=version,
|
||||
songId=mcode,
|
||||
index=index,
|
||||
chartId=chart,
|
||||
title=title,
|
||||
artist=artist,
|
||||
category=category,
|
||||
level=level,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
title = title,
|
||||
artist = artist,
|
||||
category = category,
|
||||
level = level
|
||||
title=title, artist=artist, category=category, level=level
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_music(self, version: int, song_id: Optional[int] = None) -> Optional[List[Row]]:
|
||||
|
||||
def get_music(
|
||||
self, version: int, song_id: Optional[int] = None
|
||||
) -> Optional[List[Row]]:
|
||||
if song_id is None:
|
||||
sql = select(music).where(music.c.version == version)
|
||||
else:
|
||||
sql = select(music).where(and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
))
|
||||
sql = select(music).where(
|
||||
and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_music_chart(self, version: int, song_id: int, chart_id: int) -> Optional[List[Row]]:
|
||||
sql = select(music).where(and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
music.c.chartId == chart_id
|
||||
))
|
||||
|
||||
def get_music_chart(
|
||||
self, version: int, song_id: int, chart_id: int
|
||||
) -> Optional[List[Row]]:
|
||||
sql = select(music).where(
|
||||
and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
music.c.chartId == chart_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
@@ -6,13 +6,5 @@ from titles.diva.read import DivaReader
|
||||
index = DivaServlet
|
||||
database = DivaData
|
||||
reader = DivaReader
|
||||
|
||||
use_default_title = True
|
||||
include_protocol = True
|
||||
title_secure = False
|
||||
game_codes = [DivaConstants.GAME_CODE]
|
||||
trailing_slash = True
|
||||
use_default_host = False
|
||||
host = ""
|
||||
|
||||
current_schema_version = 1
|
||||
current_schema_version = 1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from datetime import datetime
|
||||
from typing import Any, List, Dict
|
||||
import logging
|
||||
import logging
|
||||
import json
|
||||
from urllib import parse
|
||||
|
||||
@@ -10,19 +10,20 @@ from titles.diva.const import DivaConstants
|
||||
from titles.diva.database import DivaData
|
||||
from titles.diva.handlers import *
|
||||
|
||||
class DivaBase():
|
||||
|
||||
class DivaBase:
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: DivaConfig) -> None:
|
||||
self.core_cfg = cfg # Config file
|
||||
self.core_cfg = cfg # Config file
|
||||
self.game_config = game_cfg
|
||||
self.data = DivaData(cfg) # Database
|
||||
self.data = DivaData(cfg) # Database
|
||||
self.date_time_format = "%Y-%m-%d %H:%M:%S"
|
||||
self.logger = logging.getLogger("diva")
|
||||
self.game = DivaConstants.GAME_CODE
|
||||
self.version = DivaConstants.VER_PROJECT_DIVA_ARCADE_FUTURE_TONE
|
||||
|
||||
dt = datetime.now()
|
||||
self.time_lut=parse.quote(dt.strftime("%Y-%m-%d %H:%M:%S:16.0"))
|
||||
|
||||
self.time_lut = parse.quote(dt.strftime("%Y-%m-%d %H:%M:%S:16.0"))
|
||||
|
||||
def handle_test_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
@@ -32,12 +33,12 @@ class DivaBase():
|
||||
def handle_attend_request(self, data: bytes) -> str:
|
||||
encoded = "&"
|
||||
params = {
|
||||
'atnd_prm1': '0,1,1,0,0,0,1,0,100,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1',
|
||||
'atnd_prm2': '30,10,100,4,1,50,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1',
|
||||
'atnd_prm3': '100,0,1,1,1,1,1,1,1,1,2,3,4,1,1,1,3,4,5,1,1,1,4,5,6,1,1,1,5,6,7,4,4,4,9,10,14,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,10,30,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0',
|
||||
'atnd_lut': f'{self.time_lut}',
|
||||
"atnd_prm1": "0,1,1,0,0,0,1,0,100,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
|
||||
"atnd_prm2": "30,10,100,4,1,50,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
|
||||
"atnd_prm3": "100,0,1,1,1,1,1,1,1,1,2,3,4,1,1,1,3,4,5,1,1,1,4,5,6,1,1,1,5,6,7,4,4,4,9,10,14,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,10,30,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"atnd_lut": f"{self.time_lut}",
|
||||
}
|
||||
|
||||
|
||||
encoded += parse.urlencode(params)
|
||||
encoded = encoded.replace("%2C", ",")
|
||||
|
||||
@@ -46,42 +47,42 @@ class DivaBase():
|
||||
def handle_ping_request(self, data: bytes) -> str:
|
||||
encoded = "&"
|
||||
params = {
|
||||
'ping_b_msg': f'Welcome to {self.core_cfg.server.name} network!',
|
||||
'ping_m_msg': 'xxx',
|
||||
'atnd_lut': f'{self.time_lut}',
|
||||
'fi_lut': f'{self.time_lut}',
|
||||
'ci_lut': f'{self.time_lut}',
|
||||
'qi_lut': f'{self.time_lut}',
|
||||
'pvl_lut': '2021-05-22 12:08:16.0',
|
||||
'shp_ctlg_lut': '2020-06-10 19:44:16.0',
|
||||
'cstmz_itm_ctlg_lut': '2019-10-08 20:23:12.0',
|
||||
'ngwl_lut': '2019-10-08 20:23:12.0',
|
||||
'rnk_nv_lut': '2020-06-10 19:51:30.0',
|
||||
'rnk_ps_lut': f'{self.time_lut}',
|
||||
'bi_lut': '2020-09-18 10:00:00.0',
|
||||
'cpi_lut': '2020-10-25 09:25:10.0',
|
||||
'bdlol_lut': '2020-09-18 10:00:00.0',
|
||||
'p_std_hc_lut': '2019-08-01 04:00:36.0',
|
||||
'p_std_i_n_lut': '2019-08-01 04:00:36.0',
|
||||
'pdcl_lut': '2019-08-01 04:00:36.0',
|
||||
'pnml_lut': '2019-08-01 04:00:36.0',
|
||||
'cinml_lut': '2019-08-01 04:00:36.0',
|
||||
'rwl_lut': '2019-08-01 04:00:36.0',
|
||||
'req_inv_cmd_num': '-1,-1,-1,-1,-1,-1,-1,-1,-1,-1',
|
||||
'req_inv_cmd_prm1': '-1,-1,-1,-1,-1,-1,-1,-1,-1,-1',
|
||||
'req_inv_cmd_prm2': '-1,-1,-1,-1,-1,-1,-1,-1,-1,-1',
|
||||
'req_inv_cmd_prm3': '-1,-1,-1,-1,-1,-1,-1,-1,-1,-1',
|
||||
'req_inv_cmd_prm4': '-1,-1,-1,-1,-1,-1,-1,-1,-1,-1',
|
||||
'pow_save_flg': 0,
|
||||
'nblss_dnt_p': 100,
|
||||
'nblss_ltt_rl_vp': 1500,
|
||||
'nblss_ex_ltt_flg': 1,
|
||||
'nblss_dnt_st_tm': "2019-07-15 12:00:00.0",
|
||||
'nblss_dnt_ed_tm': "2019-09-17 12:00:00.0",
|
||||
'nblss_ltt_st_tm': "2019-09-18 12:00:00.0",
|
||||
'nblss_ltt_ed_tm': "2019-09-22 12:00:00.0",
|
||||
"ping_b_msg": f"Welcome to {self.core_cfg.server.name} network!",
|
||||
"ping_m_msg": "xxx",
|
||||
"atnd_lut": f"{self.time_lut}",
|
||||
"fi_lut": f"{self.time_lut}",
|
||||
"ci_lut": f"{self.time_lut}",
|
||||
"qi_lut": f"{self.time_lut}",
|
||||
"pvl_lut": "2021-05-22 12:08:16.0",
|
||||
"shp_ctlg_lut": "2020-06-10 19:44:16.0",
|
||||
"cstmz_itm_ctlg_lut": "2019-10-08 20:23:12.0",
|
||||
"ngwl_lut": "2019-10-08 20:23:12.0",
|
||||
"rnk_nv_lut": "2020-06-10 19:51:30.0",
|
||||
"rnk_ps_lut": f"{self.time_lut}",
|
||||
"bi_lut": "2020-09-18 10:00:00.0",
|
||||
"cpi_lut": "2020-10-25 09:25:10.0",
|
||||
"bdlol_lut": "2020-09-18 10:00:00.0",
|
||||
"p_std_hc_lut": "2019-08-01 04:00:36.0",
|
||||
"p_std_i_n_lut": "2019-08-01 04:00:36.0",
|
||||
"pdcl_lut": "2019-08-01 04:00:36.0",
|
||||
"pnml_lut": "2019-08-01 04:00:36.0",
|
||||
"cinml_lut": "2019-08-01 04:00:36.0",
|
||||
"rwl_lut": "2019-08-01 04:00:36.0",
|
||||
"req_inv_cmd_num": "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1",
|
||||
"req_inv_cmd_prm1": "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1",
|
||||
"req_inv_cmd_prm2": "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1",
|
||||
"req_inv_cmd_prm3": "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1",
|
||||
"req_inv_cmd_prm4": "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1",
|
||||
"pow_save_flg": 0,
|
||||
"nblss_dnt_p": 100,
|
||||
"nblss_ltt_rl_vp": 1500,
|
||||
"nblss_ex_ltt_flg": 1,
|
||||
"nblss_dnt_st_tm": "2019-07-15 12:00:00.0",
|
||||
"nblss_dnt_ed_tm": "2019-09-17 12:00:00.0",
|
||||
"nblss_ltt_st_tm": "2019-09-18 12:00:00.0",
|
||||
"nblss_ltt_ed_tm": "2019-09-22 12:00:00.0",
|
||||
}
|
||||
|
||||
|
||||
encoded += parse.urlencode(params)
|
||||
encoded = encoded.replace("+", "%20")
|
||||
encoded = encoded.replace("%2C", ",")
|
||||
@@ -123,7 +124,7 @@ class DivaBase():
|
||||
response += f"&pvl_lut={self.time_lut}"
|
||||
response += f"&pv_lst={pvlist}"
|
||||
|
||||
return ( response )
|
||||
return response
|
||||
|
||||
def handle_shop_catalog_request(self, data: bytes) -> str:
|
||||
catalog = ""
|
||||
@@ -138,7 +139,21 @@ class DivaBase():
|
||||
|
||||
else:
|
||||
for shop in shopList:
|
||||
line = str(shop["shopId"]) + "," + str(shop['unknown_0']) + "," + shop['name'] + "," + str(shop['points']) + "," + shop['start_date'] + "," + shop['end_date'] + "," + str(shop["type"])
|
||||
line = (
|
||||
str(shop["shopId"])
|
||||
+ ","
|
||||
+ str(shop["unknown_0"])
|
||||
+ ","
|
||||
+ shop["name"]
|
||||
+ ","
|
||||
+ str(shop["points"])
|
||||
+ ","
|
||||
+ shop["start_date"]
|
||||
+ ","
|
||||
+ shop["end_date"]
|
||||
+ ","
|
||||
+ str(shop["type"])
|
||||
)
|
||||
line = parse.quote(line) + ","
|
||||
catalog += f"{parse.quote(line)}"
|
||||
|
||||
@@ -147,7 +162,7 @@ class DivaBase():
|
||||
response = f"&shp_ctlg_lut={self.time_lut}"
|
||||
response += f"&shp_ctlg={catalog[:-3]}"
|
||||
|
||||
return ( response )
|
||||
return response
|
||||
|
||||
def handle_buy_module_request(self, data: bytes) -> str:
|
||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||
@@ -163,10 +178,7 @@ class DivaBase():
|
||||
|
||||
new_vcld_pts = profile["vcld_pts"] - int(data["mdl_price"])
|
||||
|
||||
self.data.profile.update_profile(
|
||||
profile["user"],
|
||||
vcld_pts=new_vcld_pts
|
||||
)
|
||||
self.data.profile.update_profile(profile["user"], vcld_pts=new_vcld_pts)
|
||||
self.data.module.put_module(data["pd_id"], self.version, data["mdl_id"])
|
||||
|
||||
# generate the mdl_have string
|
||||
@@ -192,7 +204,21 @@ class DivaBase():
|
||||
|
||||
else:
|
||||
for item in itemList:
|
||||
line = str(item["itemId"]) + "," + str(item['unknown_0']) + "," + item['name'] + "," + str(item['points']) + "," + item['start_date'] + "," + item['end_date'] + "," + str(item["type"])
|
||||
line = (
|
||||
str(item["itemId"])
|
||||
+ ","
|
||||
+ str(item["unknown_0"])
|
||||
+ ","
|
||||
+ item["name"]
|
||||
+ ","
|
||||
+ str(item["points"])
|
||||
+ ","
|
||||
+ item["start_date"]
|
||||
+ ","
|
||||
+ item["end_date"]
|
||||
+ ","
|
||||
+ str(item["type"])
|
||||
)
|
||||
line = parse.quote(line) + ","
|
||||
catalog += f"{parse.quote(line)}"
|
||||
|
||||
@@ -201,11 +227,13 @@ class DivaBase():
|
||||
response = f"&cstmz_itm_ctlg_lut={self.time_lut}"
|
||||
response += f"&cstmz_itm_ctlg={catalog[:-3]}"
|
||||
|
||||
return ( response )
|
||||
return response
|
||||
|
||||
def handle_buy_cstmz_itm_request(self, data: bytes) -> str:
|
||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||
item = self.data.static.get_enabled_item(self.version, int(data["cstmz_itm_id"]))
|
||||
item = self.data.static.get_enabled_item(
|
||||
self.version, int(data["cstmz_itm_id"])
|
||||
)
|
||||
|
||||
# make sure module is available to purchase
|
||||
if not item:
|
||||
@@ -218,15 +246,16 @@ class DivaBase():
|
||||
new_vcld_pts = profile["vcld_pts"] - int(data["cstmz_itm_price"])
|
||||
|
||||
# save new Vocaloid Points balance
|
||||
self.data.profile.update_profile(
|
||||
profile["user"],
|
||||
vcld_pts=new_vcld_pts
|
||||
self.data.profile.update_profile(profile["user"], vcld_pts=new_vcld_pts)
|
||||
|
||||
self.data.customize.put_customize_item(
|
||||
data["pd_id"], self.version, data["cstmz_itm_id"]
|
||||
)
|
||||
|
||||
self.data.customize.put_customize_item(data["pd_id"], self.version, data["cstmz_itm_id"])
|
||||
|
||||
# generate the cstmz_itm_have string
|
||||
cstmz_itm_have = self.data.customize.get_customize_items_have_string(data["pd_id"], self.version)
|
||||
cstmz_itm_have = self.data.customize.get_customize_items_have_string(
|
||||
data["pd_id"], self.version
|
||||
)
|
||||
|
||||
response = "&shp_rslt=1"
|
||||
response += f"&cstmz_itm_id={data['cstmz_itm_id']}"
|
||||
@@ -238,33 +267,33 @@ class DivaBase():
|
||||
def handle_festa_info_request(self, data: bytes) -> str:
|
||||
encoded = "&"
|
||||
params = {
|
||||
'fi_id': '1,-1',
|
||||
'fi_name': f'{self.core_cfg.server.name} Opening,xxx',
|
||||
'fi_kind': '0,0',
|
||||
'fi_difficulty': '-1,-1',
|
||||
'fi_pv_id_lst': 'ALL,ALL',
|
||||
'fi_attr': '7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
|
||||
'fi_add_vp': '20,0',
|
||||
'fi_mul_vp': '1,1',
|
||||
'fi_st': '2022-06-17 17:00:00.0,2014-07-08 18:10:11.0',
|
||||
'fi_et': '2029-01-01 10:00:00.0,2014-07-08 18:10:11.0',
|
||||
'fi_lut': '{self.time_lut}',
|
||||
"fi_id": "1,-1",
|
||||
"fi_name": f"{self.core_cfg.server.name} Opening,xxx",
|
||||
"fi_kind": "0,0",
|
||||
"fi_difficulty": "-1,-1",
|
||||
"fi_pv_id_lst": "ALL,ALL",
|
||||
"fi_attr": "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
|
||||
"fi_add_vp": "20,0",
|
||||
"fi_mul_vp": "1,1",
|
||||
"fi_st": "2022-06-17 17:00:00.0,2014-07-08 18:10:11.0",
|
||||
"fi_et": "2029-01-01 10:00:00.0,2014-07-08 18:10:11.0",
|
||||
"fi_lut": "{self.time_lut}",
|
||||
}
|
||||
|
||||
|
||||
encoded += parse.urlencode(params)
|
||||
encoded = encoded.replace("+", "%20")
|
||||
encoded = encoded.replace("%2C", ",")
|
||||
|
||||
return encoded
|
||||
|
||||
|
||||
def handle_contest_info_request(self, data: bytes) -> str:
|
||||
response = ""
|
||||
|
||||
response += f"&ci_lut={self.time_lut}"
|
||||
response += "&ci_str=%2A%2A%2A,%2A%2A%2A,%2A%2A%2A,%2A%2A%2A,%2A%2A%2A,%2A%2A%2A,%2A%2A%2A,%2A%2A%2A"
|
||||
|
||||
return ( response )
|
||||
|
||||
|
||||
return response
|
||||
|
||||
def handle_qst_inf_request(self, data: bytes) -> str:
|
||||
quest = ""
|
||||
|
||||
@@ -280,11 +309,31 @@ class DivaBase():
|
||||
response += f"&qhi_str={quest[:-1]}"
|
||||
else:
|
||||
for quests in questList:
|
||||
line = str(quests["questId"]) + "," + str(quests['quest_order']) + "," + str(quests['kind']) + "," + str(quests['unknown_0']) + "," + quests['start_datetime'] + "," + quests['end_datetime'] + "," + quests["name"] + "," + str(quests["unknown_1"]) + "," + str(quests["unknown_2"]) + "," + str(quests["quest_enable"])
|
||||
line = (
|
||||
str(quests["questId"])
|
||||
+ ","
|
||||
+ str(quests["quest_order"])
|
||||
+ ","
|
||||
+ str(quests["kind"])
|
||||
+ ","
|
||||
+ str(quests["unknown_0"])
|
||||
+ ","
|
||||
+ quests["start_datetime"]
|
||||
+ ","
|
||||
+ quests["end_datetime"]
|
||||
+ ","
|
||||
+ quests["name"]
|
||||
+ ","
|
||||
+ str(quests["unknown_1"])
|
||||
+ ","
|
||||
+ str(quests["unknown_2"])
|
||||
+ ","
|
||||
+ str(quests["quest_enable"])
|
||||
)
|
||||
quest += f"{parse.quote(line)}%0A,"
|
||||
|
||||
responseline = f"{quest[:-1]},"
|
||||
for i in range(len(questList),59):
|
||||
for i in range(len(questList), 59):
|
||||
responseline += "%2A%2A%2A%0A,"
|
||||
|
||||
response = ""
|
||||
@@ -293,44 +342,44 @@ class DivaBase():
|
||||
|
||||
response += "&qrai_str=%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1,%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1,%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1"
|
||||
|
||||
return ( response )
|
||||
|
||||
return response
|
||||
|
||||
def handle_nv_ranking_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_ps_ranking_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_ng_word_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_rmt_wp_list_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_pv_def_chr_list_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_pv_ng_mdl_list_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_cstmz_itm_ng_mdl_lst_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_banner_info_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_banner_data_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_cm_ply_info_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_pstd_h_ctrl_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_pstd_item_ng_lst_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_pre_start_request(self, data: bytes) -> str:
|
||||
req = PreStartRequest(data)
|
||||
resp = PreStartResponse(req.cmd, req.req_id, req.aime_id)
|
||||
@@ -349,15 +398,17 @@ class DivaBase():
|
||||
for k, v in profile_dict.items():
|
||||
if hasattr(resp, k):
|
||||
setattr(resp, k, v)
|
||||
|
||||
|
||||
if profile_shop is not None and profile_shop:
|
||||
resp.mdl_eqp_ary = profile_shop["mdl_eqp_ary"]
|
||||
|
||||
|
||||
return resp.make()
|
||||
|
||||
def handle_registration_request(self, data: bytes) -> str:
|
||||
self.data.profile.create_profile(self.version, data["aime_id"], data["player_name"])
|
||||
return (f"&cd_adm_result=1&pd_id={data['aime_id']}")
|
||||
self.data.profile.create_profile(
|
||||
self.version, data["aime_id"], data["player_name"]
|
||||
)
|
||||
return f"&cd_adm_result=1&pd_id={data['aime_id']}"
|
||||
|
||||
def handle_start_request(self, data: bytes) -> str:
|
||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||
@@ -368,12 +419,16 @@ class DivaBase():
|
||||
mdl_have = "F" * 250
|
||||
# generate the mdl_have string if "unlock_all_modules" is disabled
|
||||
if not self.game_config.mods.unlock_all_modules:
|
||||
mdl_have = self.data.module.get_modules_have_string(data["pd_id"], self.version)
|
||||
mdl_have = self.data.module.get_modules_have_string(
|
||||
data["pd_id"], self.version
|
||||
)
|
||||
|
||||
cstmz_itm_have = "F" * 250
|
||||
# generate the cstmz_itm_have string if "unlock_all_items" is disabled
|
||||
if not self.game_config.mods.unlock_all_items:
|
||||
cstmz_itm_have = self.data.customize.get_customize_items_have_string(data["pd_id"], self.version)
|
||||
cstmz_itm_have = self.data.customize.get_customize_items_have_string(
|
||||
data["pd_id"], self.version
|
||||
)
|
||||
|
||||
response = f"&pd_id={data['pd_id']}"
|
||||
response += "&start_result=1"
|
||||
@@ -436,15 +491,16 @@ class DivaBase():
|
||||
response += f"&mdl_eqp_ary={mdl_eqp_ary}"
|
||||
response += f"&c_itm_eqp_ary={c_itm_eqp_ary}"
|
||||
response += f"&ms_itm_flg_ary={ms_itm_flg_ary}"
|
||||
|
||||
return ( response )
|
||||
|
||||
return response
|
||||
|
||||
def handle_pd_unlock_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def handle_spend_credit_request(self, data: bytes) -> str:
|
||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||
if profile is None: return
|
||||
if profile is None:
|
||||
return
|
||||
|
||||
response = ""
|
||||
|
||||
@@ -455,10 +511,16 @@ class DivaBase():
|
||||
response += f"&lv_efct_id={profile['lv_efct_id']}"
|
||||
response += f"&lv_plt_id={profile['lv_plt_id']}"
|
||||
|
||||
return ( response )
|
||||
return response
|
||||
|
||||
def _get_pv_pd_result(self, song: int, pd_db_song: Dict, pd_db_ranking: Dict,
|
||||
pd_db_customize: Dict, edition: int) -> str:
|
||||
def _get_pv_pd_result(
|
||||
self,
|
||||
song: int,
|
||||
pd_db_song: Dict,
|
||||
pd_db_ranking: Dict,
|
||||
pd_db_customize: Dict,
|
||||
edition: int,
|
||||
) -> str:
|
||||
"""
|
||||
Helper function to generate the pv_result string for every song, ranking and edition
|
||||
"""
|
||||
@@ -467,7 +529,7 @@ class DivaBase():
|
||||
# make sure there are enough max scores to calculate a ranking
|
||||
if pd_db_ranking["ranking"] != 0:
|
||||
global_ranking = pd_db_ranking["ranking"]
|
||||
|
||||
|
||||
# pv_no
|
||||
pv_result = f"{song},"
|
||||
# edition
|
||||
@@ -497,7 +559,7 @@ class DivaBase():
|
||||
f"{pd_db_customize['chsld_se']},"
|
||||
f"{pd_db_customize['sldtch_se']}"
|
||||
)
|
||||
|
||||
|
||||
pv_result += f"{module_eqp},"
|
||||
pv_result += f"{customize_eqp},"
|
||||
pv_result += f"{customize_flag},"
|
||||
@@ -521,21 +583,35 @@ class DivaBase():
|
||||
if int(song) > 0:
|
||||
# the request do not send a edition so just perform a query best score and ranking for each edition.
|
||||
# 0=ORIGINAL, 1=EXTRA
|
||||
pd_db_song_0 = self.data.score.get_best_user_score(data["pd_id"], int(song), data["difficulty"], edition=0)
|
||||
pd_db_song_1 = self.data.score.get_best_user_score(data["pd_id"], int(song), data["difficulty"], edition=1)
|
||||
|
||||
pd_db_song_0 = self.data.score.get_best_user_score(
|
||||
data["pd_id"], int(song), data["difficulty"], edition=0
|
||||
)
|
||||
pd_db_song_1 = self.data.score.get_best_user_score(
|
||||
data["pd_id"], int(song), data["difficulty"], edition=1
|
||||
)
|
||||
|
||||
pd_db_ranking_0, pd_db_ranking_1 = None, None
|
||||
if pd_db_song_0:
|
||||
pd_db_ranking_0 = self.data.score.get_global_ranking(data["pd_id"], int(song), data["difficulty"], edition=0)
|
||||
pd_db_ranking_0 = self.data.score.get_global_ranking(
|
||||
data["pd_id"], int(song), data["difficulty"], edition=0
|
||||
)
|
||||
|
||||
if pd_db_song_1:
|
||||
pd_db_ranking_1 = self.data.score.get_global_ranking(data["pd_id"], int(song), data["difficulty"], edition=1)
|
||||
pd_db_ranking_1 = self.data.score.get_global_ranking(
|
||||
data["pd_id"], int(song), data["difficulty"], edition=1
|
||||
)
|
||||
|
||||
pd_db_customize = self.data.pv_customize.get_pv_customize(
|
||||
data["pd_id"], int(song)
|
||||
)
|
||||
|
||||
pd_db_customize = self.data.pv_customize.get_pv_customize(data["pd_id"], int(song))
|
||||
|
||||
# generate the pv_result string with the ORIGINAL edition and the EXTRA edition appended
|
||||
pv_result = self._get_pv_pd_result(int(song), pd_db_song_0, pd_db_ranking_0, pd_db_customize, edition=0)
|
||||
pv_result += "," + self._get_pv_pd_result(int(song), pd_db_song_1, pd_db_ranking_1, pd_db_customize, edition=1)
|
||||
pv_result = self._get_pv_pd_result(
|
||||
int(song), pd_db_song_0, pd_db_ranking_0, pd_db_customize, edition=0
|
||||
)
|
||||
pv_result += "," + self._get_pv_pd_result(
|
||||
int(song), pd_db_song_1, pd_db_ranking_1, pd_db_customize, edition=1
|
||||
)
|
||||
|
||||
self.logger.debug(f"pv_result = {pv_result}")
|
||||
|
||||
@@ -549,13 +625,12 @@ class DivaBase():
|
||||
response += "&pdddt_flg=0"
|
||||
response += f"&pdddt_tm={self.time_lut}"
|
||||
|
||||
return ( response )
|
||||
return response
|
||||
|
||||
def handle_stage_start_request(self, data: bytes) -> str:
|
||||
pass
|
||||
|
||||
def handle_stage_result_request(self, data: bytes) -> str:
|
||||
|
||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||
|
||||
pd_song_list = data["stg_ply_pv_id"].split(",")
|
||||
@@ -574,15 +649,100 @@ class DivaBase():
|
||||
|
||||
for index, value in enumerate(pd_song_list):
|
||||
if "-1" not in pd_song_list[index]:
|
||||
profile_pd_db_song = self.data.score.get_best_user_score(data["pd_id"], pd_song_list[index], pd_song_difficulty[index], pd_song_edition[index])
|
||||
profile_pd_db_song = self.data.score.get_best_user_score(
|
||||
data["pd_id"],
|
||||
pd_song_list[index],
|
||||
pd_song_difficulty[index],
|
||||
pd_song_edition[index],
|
||||
)
|
||||
if profile_pd_db_song is None:
|
||||
self.data.score.put_best_score(data["pd_id"], self.version, pd_song_list[index], pd_song_difficulty[index], pd_song_edition[index], pd_song_max_score[index], pd_song_max_atn_pnt[index], pd_song_ranking[index], pd_song_sort_kind, pd_song_cool_cnt[index], pd_song_fine_cnt[index], pd_song_safe_cnt[index], pd_song_sad_cnt[index], pd_song_worst_cnt[index], pd_song_max_combo[index])
|
||||
self.data.score.put_playlog(data["pd_id"], self.version, pd_song_list[index], pd_song_difficulty[index], pd_song_edition[index], pd_song_max_score[index], pd_song_max_atn_pnt[index], pd_song_ranking[index], pd_song_sort_kind, pd_song_cool_cnt[index], pd_song_fine_cnt[index], pd_song_safe_cnt[index], pd_song_sad_cnt[index], pd_song_worst_cnt[index], pd_song_max_combo[index])
|
||||
self.data.score.put_best_score(
|
||||
data["pd_id"],
|
||||
self.version,
|
||||
pd_song_list[index],
|
||||
pd_song_difficulty[index],
|
||||
pd_song_edition[index],
|
||||
pd_song_max_score[index],
|
||||
pd_song_max_atn_pnt[index],
|
||||
pd_song_ranking[index],
|
||||
pd_song_sort_kind,
|
||||
pd_song_cool_cnt[index],
|
||||
pd_song_fine_cnt[index],
|
||||
pd_song_safe_cnt[index],
|
||||
pd_song_sad_cnt[index],
|
||||
pd_song_worst_cnt[index],
|
||||
pd_song_max_combo[index],
|
||||
)
|
||||
self.data.score.put_playlog(
|
||||
data["pd_id"],
|
||||
self.version,
|
||||
pd_song_list[index],
|
||||
pd_song_difficulty[index],
|
||||
pd_song_edition[index],
|
||||
pd_song_max_score[index],
|
||||
pd_song_max_atn_pnt[index],
|
||||
pd_song_ranking[index],
|
||||
pd_song_sort_kind,
|
||||
pd_song_cool_cnt[index],
|
||||
pd_song_fine_cnt[index],
|
||||
pd_song_safe_cnt[index],
|
||||
pd_song_sad_cnt[index],
|
||||
pd_song_worst_cnt[index],
|
||||
pd_song_max_combo[index],
|
||||
)
|
||||
elif int(pd_song_max_score[index]) >= int(profile_pd_db_song["score"]):
|
||||
self.data.score.put_best_score(data["pd_id"], self.version, pd_song_list[index], pd_song_difficulty[index], pd_song_edition[index], pd_song_max_score[index], pd_song_max_atn_pnt[index], pd_song_ranking[index], pd_song_sort_kind, pd_song_cool_cnt[index], pd_song_fine_cnt[index], pd_song_safe_cnt[index], pd_song_sad_cnt[index], pd_song_worst_cnt[index], pd_song_max_combo[index])
|
||||
self.data.score.put_playlog(data["pd_id"], self.version, pd_song_list[index], pd_song_difficulty[index], pd_song_edition[index], pd_song_max_score[index], pd_song_max_atn_pnt[index], pd_song_ranking[index], pd_song_sort_kind, pd_song_cool_cnt[index], pd_song_fine_cnt[index], pd_song_safe_cnt[index], pd_song_sad_cnt[index], pd_song_worst_cnt[index], pd_song_max_combo[index])
|
||||
self.data.score.put_best_score(
|
||||
data["pd_id"],
|
||||
self.version,
|
||||
pd_song_list[index],
|
||||
pd_song_difficulty[index],
|
||||
pd_song_edition[index],
|
||||
pd_song_max_score[index],
|
||||
pd_song_max_atn_pnt[index],
|
||||
pd_song_ranking[index],
|
||||
pd_song_sort_kind,
|
||||
pd_song_cool_cnt[index],
|
||||
pd_song_fine_cnt[index],
|
||||
pd_song_safe_cnt[index],
|
||||
pd_song_sad_cnt[index],
|
||||
pd_song_worst_cnt[index],
|
||||
pd_song_max_combo[index],
|
||||
)
|
||||
self.data.score.put_playlog(
|
||||
data["pd_id"],
|
||||
self.version,
|
||||
pd_song_list[index],
|
||||
pd_song_difficulty[index],
|
||||
pd_song_edition[index],
|
||||
pd_song_max_score[index],
|
||||
pd_song_max_atn_pnt[index],
|
||||
pd_song_ranking[index],
|
||||
pd_song_sort_kind,
|
||||
pd_song_cool_cnt[index],
|
||||
pd_song_fine_cnt[index],
|
||||
pd_song_safe_cnt[index],
|
||||
pd_song_sad_cnt[index],
|
||||
pd_song_worst_cnt[index],
|
||||
pd_song_max_combo[index],
|
||||
)
|
||||
elif int(pd_song_max_score[index]) != int(profile_pd_db_song["score"]):
|
||||
self.data.score.put_playlog(data["pd_id"], self.version, pd_song_list[index], pd_song_difficulty[index], pd_song_edition[index], pd_song_max_score[index], pd_song_max_atn_pnt[index], pd_song_ranking[index], pd_song_sort_kind, pd_song_cool_cnt[index], pd_song_fine_cnt[index], pd_song_safe_cnt[index], pd_song_sad_cnt[index], pd_song_worst_cnt[index], pd_song_max_combo[index])
|
||||
self.data.score.put_playlog(
|
||||
data["pd_id"],
|
||||
self.version,
|
||||
pd_song_list[index],
|
||||
pd_song_difficulty[index],
|
||||
pd_song_edition[index],
|
||||
pd_song_max_score[index],
|
||||
pd_song_max_atn_pnt[index],
|
||||
pd_song_ranking[index],
|
||||
pd_song_sort_kind,
|
||||
pd_song_cool_cnt[index],
|
||||
pd_song_fine_cnt[index],
|
||||
pd_song_safe_cnt[index],
|
||||
pd_song_sad_cnt[index],
|
||||
pd_song_worst_cnt[index],
|
||||
pd_song_max_combo[index],
|
||||
)
|
||||
|
||||
# Profile saving based on registration list
|
||||
|
||||
@@ -592,7 +752,7 @@ class DivaBase():
|
||||
total_atn_pnt = 0
|
||||
for best_score in best_scores:
|
||||
total_atn_pnt += best_score["atn_pnt"]
|
||||
|
||||
|
||||
new_level = (total_atn_pnt // 13979) + 1
|
||||
new_level_pnt = round((total_atn_pnt % 13979) / 13979 * 100)
|
||||
|
||||
@@ -614,7 +774,7 @@ class DivaBase():
|
||||
nxt_dffclty=int(data["nxt_dffclty"]),
|
||||
nxt_edtn=int(data["nxt_edtn"]),
|
||||
my_qst_id=data["my_qst_id"],
|
||||
my_qst_sts=data["my_qst_sts"]
|
||||
my_qst_sts=data["my_qst_sts"],
|
||||
)
|
||||
|
||||
response += f"&lv_num={new_level}"
|
||||
@@ -647,35 +807,51 @@ class DivaBase():
|
||||
response += "&my_ccd_r_hnd=-1,-1,-1,-1,-1"
|
||||
response += "&my_ccd_r_vp=-1,-1,-1,-1,-1"
|
||||
|
||||
return ( response )
|
||||
return response
|
||||
|
||||
def handle_end_request(self, data: bytes) -> str:
|
||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||
|
||||
self.data.profile.update_profile(
|
||||
profile["user"],
|
||||
my_qst_id=data["my_qst_id"],
|
||||
my_qst_sts=data["my_qst_sts"]
|
||||
profile["user"], my_qst_id=data["my_qst_id"], my_qst_sts=data["my_qst_sts"]
|
||||
)
|
||||
return (f'')
|
||||
return f""
|
||||
|
||||
def handle_shop_exit_request(self, data: bytes) -> str:
|
||||
self.data.item.put_shop(data["pd_id"], self.version, data["mdl_eqp_cmn_ary"], data["c_itm_eqp_cmn_ary"], data["ms_itm_flg_cmn_ary"])
|
||||
self.data.item.put_shop(
|
||||
data["pd_id"],
|
||||
self.version,
|
||||
data["mdl_eqp_cmn_ary"],
|
||||
data["c_itm_eqp_cmn_ary"],
|
||||
data["ms_itm_flg_cmn_ary"],
|
||||
)
|
||||
if int(data["use_pv_mdl_eqp"]) == 1:
|
||||
self.data.pv_customize.put_pv_customize(data["pd_id"], self.version, data["ply_pv_id"],
|
||||
data["mdl_eqp_pv_ary"], data["c_itm_eqp_pv_ary"], data["ms_itm_flg_pv_ary"])
|
||||
self.data.pv_customize.put_pv_customize(
|
||||
data["pd_id"],
|
||||
self.version,
|
||||
data["ply_pv_id"],
|
||||
data["mdl_eqp_pv_ary"],
|
||||
data["c_itm_eqp_pv_ary"],
|
||||
data["ms_itm_flg_pv_ary"],
|
||||
)
|
||||
else:
|
||||
self.data.pv_customize.put_pv_customize(data["pd_id"], self.version, data["ply_pv_id"],
|
||||
"-1,-1,-1", "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1", "1,1,1,1,1,1,1,1,1,1,1,1")
|
||||
self.data.pv_customize.put_pv_customize(
|
||||
data["pd_id"],
|
||||
self.version,
|
||||
data["ply_pv_id"],
|
||||
"-1,-1,-1",
|
||||
"-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1",
|
||||
"1,1,1,1,1,1,1,1,1,1,1,1",
|
||||
)
|
||||
|
||||
response = "&shp_rslt=1"
|
||||
return ( response )
|
||||
return response
|
||||
|
||||
def handle_card_procedure_request(self, data: Dict) -> str:
|
||||
profile = self.data.profile.get_profile(data["aime_id"], self.version)
|
||||
if profile is None:
|
||||
return "&cd_adm_result=0"
|
||||
|
||||
|
||||
response = "&cd_adm_result=1"
|
||||
response += "&chg_name_price=100"
|
||||
response += "&accept_idx=100"
|
||||
@@ -690,20 +866,18 @@ class DivaBase():
|
||||
response += f"&passwd_stat={profile['passwd_stat']}"
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def handle_change_name_request(self, data: Dict) -> str:
|
||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||
|
||||
# make sure user has enough Vocaloid Points
|
||||
if profile["vcld_pts"] < int(data["chg_name_price"]):
|
||||
return "&cd_adm_result=0"
|
||||
|
||||
|
||||
# update the vocaloid points and player name
|
||||
new_vcld_pts = profile["vcld_pts"] - int(data["chg_name_price"])
|
||||
self.data.profile.update_profile(
|
||||
profile["user"],
|
||||
player_name=data["player_name"],
|
||||
vcld_pts=new_vcld_pts
|
||||
profile["user"], player_name=data["player_name"], vcld_pts=new_vcld_pts
|
||||
)
|
||||
|
||||
response = "&cd_adm_result=1"
|
||||
@@ -712,19 +886,17 @@ class DivaBase():
|
||||
response += f"&player_name={data['player_name']}"
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def handle_change_passwd_request(self, data: Dict) -> str:
|
||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||
|
||||
# TODO: return correct error number instead of 0
|
||||
if (data["passwd"] != profile["passwd"]):
|
||||
if data["passwd"] != profile["passwd"]:
|
||||
return "&cd_adm_result=0"
|
||||
|
||||
# set password to true and update the saved password
|
||||
self.data.profile.update_profile(
|
||||
profile["user"],
|
||||
passwd_stat=1,
|
||||
passwd=data["new_passwd"]
|
||||
profile["user"], passwd_stat=1, passwd=data["new_passwd"]
|
||||
)
|
||||
|
||||
response = "&cd_adm_result=1"
|
||||
|
||||
@@ -1,30 +1,40 @@
|
||||
from core.config import CoreConfig
|
||||
|
||||
|
||||
class DivaServerConfig():
|
||||
class DivaServerConfig:
|
||||
def __init__(self, parent_config: "DivaConfig") -> None:
|
||||
self.__config = parent_config
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(self.__config, 'diva', 'server', 'enable', default=True)
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "diva", "server", "enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def loglevel(self) -> int:
|
||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'diva', 'server', 'loglevel', default="info"))
|
||||
return CoreConfig.str_to_loglevel(
|
||||
CoreConfig.get_config_field(
|
||||
self.__config, "diva", "server", "loglevel", default="info"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class DivaModsConfig():
|
||||
class DivaModsConfig:
|
||||
def __init__(self, parent_config: "DivaConfig") -> None:
|
||||
self.__config = parent_config
|
||||
|
||||
@property
|
||||
def unlock_all_modules(self) -> bool:
|
||||
return CoreConfig.get_config_field(self.__config, 'diva', 'mods', 'unlock_all_modules', default=True)
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "diva", "mods", "unlock_all_modules", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def unlock_all_items(self) -> bool:
|
||||
return CoreConfig.get_config_field(self.__config, 'diva', 'mods', 'unlock_all_items', default=True)
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "diva", "mods", "unlock_all_items", default=True
|
||||
)
|
||||
|
||||
|
||||
class DivaConfig(dict):
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
class DivaConstants():
|
||||
class DivaConstants:
|
||||
GAME_CODE = "SBZV"
|
||||
|
||||
CONFIG_NAME = "diva.yaml"
|
||||
|
||||
VER_PROJECT_DIVA_ARCADE = 0
|
||||
VER_PROJECT_DIVA_ARCADE_FUTURE_TONE = 1
|
||||
|
||||
@@ -8,4 +10,4 @@ class DivaConstants():
|
||||
|
||||
@classmethod
|
||||
def game_ver_to_string(cls, ver: int):
|
||||
return cls.VERSION_NAMES[ver]
|
||||
return cls.VERSION_NAMES[ver]
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
from core.data import Data
|
||||
from core.config import CoreConfig
|
||||
from titles.diva.schema import DivaProfileData, DivaScoreData, DivaModuleData, DivaCustomizeItemData, DivaPvCustomizeData, DivaItemData, DivaStaticData
|
||||
from titles.diva.schema import (
|
||||
DivaProfileData,
|
||||
DivaScoreData,
|
||||
DivaModuleData,
|
||||
DivaCustomizeItemData,
|
||||
DivaPvCustomizeData,
|
||||
DivaItemData,
|
||||
DivaStaticData,
|
||||
)
|
||||
|
||||
|
||||
class DivaData(Data):
|
||||
|
||||
@@ -5,37 +5,72 @@ from logging.handlers import TimedRotatingFileHandler
|
||||
import zlib
|
||||
import json
|
||||
import base64
|
||||
from os import path
|
||||
from typing import Tuple
|
||||
|
||||
from titles.diva.handlers.base import *
|
||||
from core.config import CoreConfig
|
||||
from titles.diva.config import DivaConfig
|
||||
from titles.diva.const import DivaConstants
|
||||
from titles.diva.base import DivaBase
|
||||
|
||||
class DivaServlet():
|
||||
|
||||
class DivaServlet:
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.game_cfg = DivaConfig()
|
||||
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/diva.yaml")))
|
||||
if path.exists(f"{cfg_dir}/{DivaConstants.CONFIG_NAME}"):
|
||||
self.game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{DivaConstants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
self.base = DivaBase(core_cfg, self.game_cfg)
|
||||
|
||||
self.logger = logging.getLogger("diva")
|
||||
log_fmt_str = "[%(asctime)s] Diva | %(levelname)s | %(message)s"
|
||||
log_fmt = logging.Formatter(log_fmt_str)
|
||||
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "diva"), encoding='utf8',
|
||||
when="d", backupCount=10)
|
||||
fileHandler = TimedRotatingFileHandler(
|
||||
"{0}/{1}.log".format(self.core_cfg.server.log_dir, "diva"),
|
||||
encoding="utf8",
|
||||
when="d",
|
||||
backupCount=10,
|
||||
)
|
||||
|
||||
fileHandler.setFormatter(log_fmt)
|
||||
|
||||
|
||||
consoleHandler = logging.StreamHandler()
|
||||
consoleHandler.setFormatter(log_fmt)
|
||||
|
||||
self.logger.addHandler(fileHandler)
|
||||
self.logger.addHandler(consoleHandler)
|
||||
|
||||
|
||||
self.logger.setLevel(self.game_cfg.server.loglevel)
|
||||
coloredlogs.install(level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str)
|
||||
|
||||
coloredlogs.install(
|
||||
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_allnet_info(
|
||||
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
|
||||
) -> Tuple[bool, str, str]:
|
||||
game_cfg = DivaConfig()
|
||||
if path.exists(f"{cfg_dir}/{DivaConstants.CONFIG_NAME}"):
|
||||
game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{DivaConstants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
if not game_cfg.server.enable:
|
||||
return (False, "", "")
|
||||
|
||||
if core_cfg.server.is_develop:
|
||||
return (
|
||||
True,
|
||||
f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/",
|
||||
"",
|
||||
)
|
||||
|
||||
return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v/", "")
|
||||
|
||||
def render_POST(self, req: Request, version: int, url_path: str) -> bytes:
|
||||
req_raw = req.content.getvalue()
|
||||
url_header = req.getAllHeaders()
|
||||
@@ -44,40 +79,46 @@ class DivaServlet():
|
||||
if "THIS_STRING_SEPARATES" in str(url_header):
|
||||
binary_request = req_raw.splitlines()
|
||||
binary_cmd_decoded = binary_request[3].decode("utf-8")
|
||||
|
||||
|
||||
req_cls = BaseBinaryRequest(binary_cmd_decoded)
|
||||
|
||||
else:
|
||||
json_string = json.dumps(req_raw.decode("utf-8")) #Take the response and decode as UTF-8 and dump
|
||||
b64string = json_string.replace(r'\n', '\n') # Remove all \n and separate them as new lines
|
||||
gz_string = base64.b64decode(b64string) # Decompressing the base64 string
|
||||
|
||||
json_string = json.dumps(
|
||||
req_raw.decode("utf-8")
|
||||
) # Take the response and decode as UTF-8 and dump
|
||||
b64string = json_string.replace(
|
||||
r"\n", "\n"
|
||||
) # Remove all \n and separate them as new lines
|
||||
gz_string = base64.b64decode(b64string) # Decompressing the base64 string
|
||||
|
||||
try:
|
||||
url_data = zlib.decompress( gz_string ) # Decompressing the gzip
|
||||
url_data = zlib.decompress(gz_string) # Decompressing the gzip
|
||||
except zlib.error as e:
|
||||
self.logger.error(f"Failed to defalte! {e} -> {gz_string}")
|
||||
return b"stat=0"
|
||||
|
||||
|
||||
try:
|
||||
req_cls = BaseRequest(url_data)
|
||||
except DivaRequestParseException as e:
|
||||
self.logger.error(e)
|
||||
return b"stat=0"
|
||||
|
||||
|
||||
self.logger.debug(f"Request: {req_raw}\nHeaders: {url_header}")
|
||||
self.logger.info(f"{req_cls.cmd} request from {req_cls.b_serial} at {req.getClientAddress().host}")
|
||||
|
||||
self.logger.info(
|
||||
f"{req_cls.cmd} request from {req_cls.b_serial} at {req.getClientAddress().host}"
|
||||
)
|
||||
|
||||
handler_str = f"handle_{req_cls.cmd}_request"
|
||||
|
||||
if not hasattr(self.base, handler_str):
|
||||
self.logger.warn(f"Unhandled cmd {req_cls.cmd}")
|
||||
return BaseResponse(req_cls.cmd, req_cls.req_id).make().encode()
|
||||
|
||||
|
||||
handler = getattr(self.base, handler_str)
|
||||
|
||||
response = handler(req_cls.raw)
|
||||
if response is None or response == "":
|
||||
response = BaseResponse(req_cls.cmd, req_cls.req_id).make()
|
||||
|
||||
self.logger.debug(f"Response: {response}")
|
||||
|
||||
self.logger.debug(f"Response: {response}")
|
||||
return response.encode(errors="ignore")
|
||||
|
||||
@@ -7,13 +7,23 @@ from core.config import CoreConfig
|
||||
from titles.diva.database import DivaData
|
||||
from titles.diva.const import DivaConstants
|
||||
|
||||
|
||||
class DivaReader(BaseReader):
|
||||
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str], opt_dir: Optional[str], extra: Optional[str]) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
config: CoreConfig,
|
||||
version: int,
|
||||
bin_dir: Optional[str],
|
||||
opt_dir: Optional[str],
|
||||
extra: Optional[str],
|
||||
) -> None:
|
||||
super().__init__(config, version, bin_dir, opt_dir, extra)
|
||||
self.data = DivaData(config)
|
||||
|
||||
try:
|
||||
self.logger.info(f"Start importer for {DivaConstants.game_ver_to_string(version)}")
|
||||
self.logger.info(
|
||||
f"Start importer for {DivaConstants.game_ver_to_string(version)}"
|
||||
)
|
||||
except IndexError:
|
||||
self.logger.error(f"Invalid project diva version {version}")
|
||||
exit(1)
|
||||
@@ -30,7 +40,7 @@ class DivaReader(BaseReader):
|
||||
if not path.exists(f"{self.bin_dir}/rom"):
|
||||
self.logger.warn(f"Couldn't find rom folder in {self.bin_dir}, skipping")
|
||||
pull_bin_rom = False
|
||||
|
||||
|
||||
if self.opt_dir is not None:
|
||||
opt_dirs = self.get_data_directories(self.opt_dir)
|
||||
else:
|
||||
@@ -44,18 +54,25 @@ class DivaReader(BaseReader):
|
||||
if pull_opt_rom:
|
||||
for dir in opt_dirs:
|
||||
self.read_rom(f"{dir}/rom")
|
||||
|
||||
|
||||
def read_ram(self, ram_root_dir: str) -> None:
|
||||
self.logger.info(f"Read RAM from {ram_root_dir}")
|
||||
|
||||
if path.exists(f"{ram_root_dir}/databank"):
|
||||
for root, dirs, files in walk(f"{ram_root_dir}/databank"):
|
||||
for file in files:
|
||||
if file.startswith("ShopCatalog_") or file.startswith("CustomizeItemCatalog_") or \
|
||||
(file.startswith("QuestInfo") and not file.startswith("QuestInfoTm")):
|
||||
|
||||
if (
|
||||
file.startswith("ShopCatalog_")
|
||||
or file.startswith("CustomizeItemCatalog_")
|
||||
or (
|
||||
file.startswith("QuestInfo")
|
||||
and not file.startswith("QuestInfoTm")
|
||||
)
|
||||
):
|
||||
with open(f"{root}/{file}", "r") as f:
|
||||
file_data: str = urllib.parse.unquote(urllib.parse.unquote(f.read()))
|
||||
file_data: str = urllib.parse.unquote(
|
||||
urllib.parse.unquote(f.read())
|
||||
)
|
||||
if file_data == "***":
|
||||
self.logger.info(f"{file} is empty, skipping")
|
||||
continue
|
||||
@@ -70,23 +87,54 @@ class DivaReader(BaseReader):
|
||||
|
||||
if file.startswith("ShopCatalog_"):
|
||||
for x in range(0, len(split), 7):
|
||||
self.logger.info(f"Added shop item {split[x+0]}")
|
||||
self.logger.info(
|
||||
f"Added shop item {split[x+0]}"
|
||||
)
|
||||
|
||||
self.data.static.put_shop(self.version, split[x+0], split[x+2], split[x+6], split[x+3],
|
||||
split[x+1], split[x+4], split[x+5])
|
||||
self.data.static.put_shop(
|
||||
self.version,
|
||||
split[x + 0],
|
||||
split[x + 2],
|
||||
split[x + 6],
|
||||
split[x + 3],
|
||||
split[x + 1],
|
||||
split[x + 4],
|
||||
split[x + 5],
|
||||
)
|
||||
|
||||
elif file.startswith("CustomizeItemCatalog_") and len(split) >= 7:
|
||||
elif (
|
||||
file.startswith("CustomizeItemCatalog_")
|
||||
and len(split) >= 7
|
||||
):
|
||||
for x in range(0, len(split), 7):
|
||||
self.logger.info(f"Added item {split[x+0]}")
|
||||
|
||||
self.data.static.put_items(self.version, split[x+0], split[x+2], split[x+6], split[x+3],
|
||||
split[x+1], split[x+4], split[x+5])
|
||||
self.data.static.put_items(
|
||||
self.version,
|
||||
split[x + 0],
|
||||
split[x + 2],
|
||||
split[x + 6],
|
||||
split[x + 3],
|
||||
split[x + 1],
|
||||
split[x + 4],
|
||||
split[x + 5],
|
||||
)
|
||||
|
||||
elif file.startswith("QuestInfo") and len(split) >= 9:
|
||||
self.logger.info(f"Added quest {split[0]}")
|
||||
|
||||
self.data.static.put_quests(self.version, split[0], split[6], split[2], split[3],
|
||||
split[7], split[8], split[1], split[4], split[5])
|
||||
|
||||
self.data.static.put_quests(
|
||||
self.version,
|
||||
split[0],
|
||||
split[6],
|
||||
split[2],
|
||||
split[3],
|
||||
split[7],
|
||||
split[8],
|
||||
split[1],
|
||||
split[4],
|
||||
split[5],
|
||||
)
|
||||
|
||||
else:
|
||||
continue
|
||||
@@ -102,13 +150,13 @@ class DivaReader(BaseReader):
|
||||
elif path.exists(f"{rom_root_dir}/pv_db.txt"):
|
||||
file_path = f"{rom_root_dir}/pv_db.txt"
|
||||
else:
|
||||
self.logger.warn(f"Cannot find pv_db.txt or mdata_pv_db.txt in {rom_root_dir}, skipping")
|
||||
self.logger.warn(
|
||||
f"Cannot find pv_db.txt or mdata_pv_db.txt in {rom_root_dir}, skipping"
|
||||
)
|
||||
return
|
||||
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
|
||||
for line in f.readlines():
|
||||
|
||||
if line.startswith("#") or not line:
|
||||
continue
|
||||
|
||||
@@ -127,14 +175,13 @@ class DivaReader(BaseReader):
|
||||
|
||||
for x in range(1, len(key_split)):
|
||||
key_args.append(key_split[x])
|
||||
|
||||
|
||||
try:
|
||||
pv_list[pv_id] = self.add_branch(pv_list[pv_id], key_args, val)
|
||||
except KeyError:
|
||||
pv_list[pv_id] = {}
|
||||
pv_list[pv_id] = self.add_branch(pv_list[pv_id], key_args, val)
|
||||
|
||||
|
||||
for pv_id, pv_data in pv_list.items():
|
||||
song_id = int(pv_id.split("_")[1])
|
||||
if "songinfo" not in pv_data:
|
||||
@@ -148,46 +195,99 @@ class DivaReader(BaseReader):
|
||||
if "music" not in pv_data["songinfo"]:
|
||||
pv_data["songinfo"]["music"] = "-"
|
||||
|
||||
if "easy" in pv_data['difficulty'] and '0' in pv_data['difficulty']['easy']:
|
||||
diff = pv_data['difficulty']['easy']['0']['level'].split('_')
|
||||
if "easy" in pv_data["difficulty"] and "0" in pv_data["difficulty"]["easy"]:
|
||||
diff = pv_data["difficulty"]["easy"]["0"]["level"].split("_")
|
||||
self.logger.info(f"Added song {song_id} chart 0")
|
||||
|
||||
self.data.static.put_music(self.version, song_id, 0, pv_data["song_name"], pv_data["songinfo"]["arranger"],
|
||||
pv_data["songinfo"]["illustrator"], pv_data["songinfo"]["lyrics"], pv_data["songinfo"]["music"],
|
||||
float(f"{diff[2]}.{diff[3]}"), pv_data["bpm"], pv_data["date"])
|
||||
|
||||
if "normal" in pv_data['difficulty'] and '0' in pv_data['difficulty']['normal']:
|
||||
diff = pv_data['difficulty']['normal']['0']['level'].split('_')
|
||||
self.data.static.put_music(
|
||||
self.version,
|
||||
song_id,
|
||||
0,
|
||||
pv_data["song_name"],
|
||||
pv_data["songinfo"]["arranger"],
|
||||
pv_data["songinfo"]["illustrator"],
|
||||
pv_data["songinfo"]["lyrics"],
|
||||
pv_data["songinfo"]["music"],
|
||||
float(f"{diff[2]}.{diff[3]}"),
|
||||
pv_data["bpm"],
|
||||
pv_data["date"],
|
||||
)
|
||||
|
||||
if (
|
||||
"normal" in pv_data["difficulty"]
|
||||
and "0" in pv_data["difficulty"]["normal"]
|
||||
):
|
||||
diff = pv_data["difficulty"]["normal"]["0"]["level"].split("_")
|
||||
self.logger.info(f"Added song {song_id} chart 1")
|
||||
|
||||
self.data.static.put_music(self.version, song_id, 1, pv_data["song_name"], pv_data["songinfo"]["arranger"],
|
||||
pv_data["songinfo"]["illustrator"], pv_data["songinfo"]["lyrics"], pv_data["songinfo"]["music"],
|
||||
float(f"{diff[2]}.{diff[3]}"), pv_data["bpm"], pv_data["date"])
|
||||
|
||||
if "hard" in pv_data['difficulty'] and '0' in pv_data['difficulty']['hard']:
|
||||
diff = pv_data['difficulty']['hard']['0']['level'].split('_')
|
||||
self.data.static.put_music(
|
||||
self.version,
|
||||
song_id,
|
||||
1,
|
||||
pv_data["song_name"],
|
||||
pv_data["songinfo"]["arranger"],
|
||||
pv_data["songinfo"]["illustrator"],
|
||||
pv_data["songinfo"]["lyrics"],
|
||||
pv_data["songinfo"]["music"],
|
||||
float(f"{diff[2]}.{diff[3]}"),
|
||||
pv_data["bpm"],
|
||||
pv_data["date"],
|
||||
)
|
||||
|
||||
if "hard" in pv_data["difficulty"] and "0" in pv_data["difficulty"]["hard"]:
|
||||
diff = pv_data["difficulty"]["hard"]["0"]["level"].split("_")
|
||||
self.logger.info(f"Added song {song_id} chart 2")
|
||||
|
||||
self.data.static.put_music(self.version, song_id, 2, pv_data["song_name"], pv_data["songinfo"]["arranger"],
|
||||
pv_data["songinfo"]["illustrator"], pv_data["songinfo"]["lyrics"], pv_data["songinfo"]["music"],
|
||||
float(f"{diff[2]}.{diff[3]}"), pv_data["bpm"], pv_data["date"])
|
||||
|
||||
if "extreme" in pv_data['difficulty']:
|
||||
if "0" in pv_data['difficulty']['extreme']:
|
||||
diff = pv_data['difficulty']['extreme']['0']['level'].split('_')
|
||||
self.data.static.put_music(
|
||||
self.version,
|
||||
song_id,
|
||||
2,
|
||||
pv_data["song_name"],
|
||||
pv_data["songinfo"]["arranger"],
|
||||
pv_data["songinfo"]["illustrator"],
|
||||
pv_data["songinfo"]["lyrics"],
|
||||
pv_data["songinfo"]["music"],
|
||||
float(f"{diff[2]}.{diff[3]}"),
|
||||
pv_data["bpm"],
|
||||
pv_data["date"],
|
||||
)
|
||||
|
||||
if "extreme" in pv_data["difficulty"]:
|
||||
if "0" in pv_data["difficulty"]["extreme"]:
|
||||
diff = pv_data["difficulty"]["extreme"]["0"]["level"].split("_")
|
||||
self.logger.info(f"Added song {song_id} chart 3")
|
||||
|
||||
self.data.static.put_music(self.version, song_id, 3, pv_data["song_name"], pv_data["songinfo"]["arranger"],
|
||||
pv_data["songinfo"]["illustrator"], pv_data["songinfo"]["lyrics"], pv_data["songinfo"]["music"],
|
||||
float(f"{diff[2]}.{diff[3]}"), pv_data["bpm"], pv_data["date"])
|
||||
self.data.static.put_music(
|
||||
self.version,
|
||||
song_id,
|
||||
3,
|
||||
pv_data["song_name"],
|
||||
pv_data["songinfo"]["arranger"],
|
||||
pv_data["songinfo"]["illustrator"],
|
||||
pv_data["songinfo"]["lyrics"],
|
||||
pv_data["songinfo"]["music"],
|
||||
float(f"{diff[2]}.{diff[3]}"),
|
||||
pv_data["bpm"],
|
||||
pv_data["date"],
|
||||
)
|
||||
|
||||
if "1" in pv_data['difficulty']['extreme']:
|
||||
diff = pv_data['difficulty']['extreme']['1']['level'].split('_')
|
||||
if "1" in pv_data["difficulty"]["extreme"]:
|
||||
diff = pv_data["difficulty"]["extreme"]["1"]["level"].split("_")
|
||||
self.logger.info(f"Added song {song_id} chart 4")
|
||||
|
||||
self.data.static.put_music(self.version, song_id, 4, pv_data["song_name"], pv_data["songinfo"]["arranger"],
|
||||
pv_data["songinfo"]["illustrator"], pv_data["songinfo"]["lyrics"], pv_data["songinfo"]["music"],
|
||||
float(f"{diff[2]}.{diff[3]}"), pv_data["bpm"], pv_data["date"])
|
||||
self.data.static.put_music(
|
||||
self.version,
|
||||
song_id,
|
||||
4,
|
||||
pv_data["song_name"],
|
||||
pv_data["songinfo"]["arranger"],
|
||||
pv_data["songinfo"]["illustrator"],
|
||||
pv_data["songinfo"]["lyrics"],
|
||||
pv_data["songinfo"]["music"],
|
||||
float(f"{diff[2]}.{diff[3]}"),
|
||||
pv_data["bpm"],
|
||||
pv_data["date"],
|
||||
)
|
||||
|
||||
def add_branch(self, tree: Dict, vector: List, value: str):
|
||||
"""
|
||||
@@ -195,9 +295,9 @@ class DivaReader(BaseReader):
|
||||
Author: iJames on StackOverflow
|
||||
"""
|
||||
key = vector[0]
|
||||
tree[key] = value \
|
||||
if len(vector) == 1 \
|
||||
else self.add_branch(tree[key] if key in tree else {},
|
||||
vector[1:],
|
||||
value)
|
||||
return tree
|
||||
tree[key] = (
|
||||
value
|
||||
if len(vector) == 1
|
||||
else self.add_branch(tree[key] if key in tree else {}, vector[1:], value)
|
||||
)
|
||||
return tree
|
||||
|
||||
@@ -6,6 +6,12 @@ from titles.diva.schema.pv_customize import DivaPvCustomizeData
|
||||
from titles.diva.schema.item import DivaItemData
|
||||
from titles.diva.schema.static import DivaStaticData
|
||||
|
||||
__all__ = [DivaProfileData, DivaScoreData, DivaModuleData,
|
||||
DivaCustomizeItemData, DivaPvCustomizeData, DivaItemData,
|
||||
DivaStaticData]
|
||||
__all__ = [
|
||||
DivaProfileData,
|
||||
DivaScoreData,
|
||||
DivaModuleData,
|
||||
DivaCustomizeItemData,
|
||||
DivaPvCustomizeData,
|
||||
DivaItemData,
|
||||
DivaStaticData,
|
||||
]
|
||||
|
||||
@@ -10,25 +10,29 @@ customize = Table(
|
||||
"diva_profile_customize_item",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("item_id", Integer, nullable=False),
|
||||
UniqueConstraint("user", "version", "item_id", name="diva_profile_customize_item_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
UniqueConstraint(
|
||||
"user", "version", "item_id", name="diva_profile_customize_item_uk"
|
||||
),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class DivaCustomizeItemData(BaseData):
|
||||
def put_customize_item(self, aime_id: int, version: int, item_id: int) -> None:
|
||||
sql = insert(customize).values(
|
||||
version=version,
|
||||
user=aime_id,
|
||||
item_id=item_id
|
||||
)
|
||||
sql = insert(customize).values(version=version, user=aime_id, item_id=item_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
self.logger.error(f"{__name__} Failed to insert diva profile customize item! aime id: {aime_id} item: {item_id}")
|
||||
self.logger.error(
|
||||
f"{__name__} Failed to insert diva profile customize item! aime id: {aime_id} item: {item_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
@@ -36,10 +40,9 @@ class DivaCustomizeItemData(BaseData):
|
||||
"""
|
||||
Given a game version and an aime id, return all the customize items, not used directly
|
||||
"""
|
||||
sql = customize.select(and_(
|
||||
customize.c.version == version,
|
||||
customize.c.user == aime_id
|
||||
))
|
||||
sql = customize.select(
|
||||
and_(customize.c.version == version, customize.c.user == aime_id)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
|
||||
@@ -11,37 +11,48 @@ shop = Table(
|
||||
"diva_profile_shop",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("mdl_eqp_ary", String(32)),
|
||||
Column("c_itm_eqp_ary", String(59)),
|
||||
Column("ms_itm_flg_ary", String(59)),
|
||||
UniqueConstraint("user", "version", name="diva_profile_shop_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class DivaItemData(BaseData):
|
||||
def put_shop(self, aime_id: int, version: int, mdl_eqp_ary: str,
|
||||
c_itm_eqp_ary: str, ms_itm_flg_ary: str) -> None:
|
||||
|
||||
def put_shop(
|
||||
self,
|
||||
aime_id: int,
|
||||
version: int,
|
||||
mdl_eqp_ary: str,
|
||||
c_itm_eqp_ary: str,
|
||||
ms_itm_flg_ary: str,
|
||||
) -> None:
|
||||
sql = insert(shop).values(
|
||||
version=version,
|
||||
user=aime_id,
|
||||
mdl_eqp_ary=mdl_eqp_ary,
|
||||
c_itm_eqp_ary=c_itm_eqp_ary,
|
||||
ms_itm_flg_ary=ms_itm_flg_ary
|
||||
ms_itm_flg_ary=ms_itm_flg_ary,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
mdl_eqp_ary=mdl_eqp_ary,
|
||||
c_itm_eqp_ary=c_itm_eqp_ary,
|
||||
ms_itm_flg_ary=ms_itm_flg_ary
|
||||
ms_itm_flg_ary=ms_itm_flg_ary,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.error(f"{__name__} Failed to insert diva profile! aime id: {aime_id} array: {mdl_eqp_ary}")
|
||||
self.logger.error(
|
||||
f"{__name__} Failed to insert diva profile! aime id: {aime_id} array: {mdl_eqp_ary}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
@@ -49,10 +60,7 @@ class DivaItemData(BaseData):
|
||||
"""
|
||||
Given a game version and either a profile or aime id, return the profile
|
||||
"""
|
||||
sql = shop.select(and_(
|
||||
shop.c.version == version,
|
||||
shop.c.user == aime_id
|
||||
))
|
||||
sql = shop.select(and_(shop.c.version == version, shop.c.user == aime_id))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
|
||||
@@ -10,25 +10,27 @@ module = Table(
|
||||
"diva_profile_module",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("module_id", Integer, nullable=False),
|
||||
UniqueConstraint("user", "version", "module_id", name="diva_profile_module_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class DivaModuleData(BaseData):
|
||||
def put_module(self, aime_id: int, version: int, module_id: int) -> None:
|
||||
sql = insert(module).values(
|
||||
version=version,
|
||||
user=aime_id,
|
||||
module_id=module_id
|
||||
)
|
||||
sql = insert(module).values(version=version, user=aime_id, module_id=module_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
self.logger.error(f"{__name__} Failed to insert diva profile module! aime id: {aime_id} module: {module_id}")
|
||||
self.logger.error(
|
||||
f"{__name__} Failed to insert diva profile module! aime id: {aime_id} module: {module_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
@@ -36,10 +38,7 @@ class DivaModuleData(BaseData):
|
||||
"""
|
||||
Given a game version and an aime id, return all the modules, not used directly
|
||||
"""
|
||||
sql = module.select(and_(
|
||||
module.c.version == version,
|
||||
module.c.user == aime_id
|
||||
))
|
||||
sql = module.select(and_(module.c.version == version, module.c.user == aime_id))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
|
||||
@@ -11,8 +11,11 @@ profile = Table(
|
||||
"diva_profile",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade",
|
||||
onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("player_name", String(10), nullable=False),
|
||||
Column("lv_str", String(24), nullable=False, server_default="Dab on 'em"),
|
||||
@@ -29,10 +32,8 @@ profile = Table(
|
||||
Column("use_pv_skn_eqp", Boolean, nullable=False, server_default="0"),
|
||||
Column("use_pv_btn_se_eqp", Boolean, nullable=False, server_default="1"),
|
||||
Column("use_pv_sld_se_eqp", Boolean, nullable=False, server_default="0"),
|
||||
Column("use_pv_chn_sld_se_eqp", Boolean,
|
||||
nullable=False, server_default="0"),
|
||||
Column("use_pv_sldr_tch_se_eqp", Boolean,
|
||||
nullable=False, server_default="0"),
|
||||
Column("use_pv_chn_sld_se_eqp", Boolean, nullable=False, server_default="0"),
|
||||
Column("use_pv_sldr_tch_se_eqp", Boolean, nullable=False, server_default="0"),
|
||||
Column("nxt_pv_id", Integer, nullable=False, server_default="708"),
|
||||
Column("nxt_dffclty", Integer, nullable=False, server_default="2"),
|
||||
Column("nxt_edtn", Integer, nullable=False, server_default="0"),
|
||||
@@ -44,35 +45,39 @@ profile = Table(
|
||||
Column("lv_plt_id", Integer, nullable=False, server_default="1"),
|
||||
Column("passwd_stat", Integer, nullable=False, server_default="0"),
|
||||
Column("passwd", String(12), nullable=False, server_default="**********"),
|
||||
Column("my_qst_id", String(
|
||||
128), server_default="-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"),
|
||||
Column("my_qst_sts", String(
|
||||
128), server_default="-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"),
|
||||
Column(
|
||||
"my_qst_id",
|
||||
String(128),
|
||||
server_default="-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1",
|
||||
),
|
||||
Column(
|
||||
"my_qst_sts",
|
||||
String(128),
|
||||
server_default="-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1",
|
||||
),
|
||||
UniqueConstraint("user", "version", name="diva_profile_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class DivaProfileData(BaseData):
|
||||
def create_profile(self, version: int, aime_id: int,
|
||||
player_name: str) -> Optional[int]:
|
||||
def create_profile(
|
||||
self, version: int, aime_id: int, player_name: str
|
||||
) -> Optional[int]:
|
||||
"""
|
||||
Given a game version, aime id, and player_name, create a profile and return it's ID
|
||||
"""
|
||||
sql = insert(profile).values(
|
||||
version=version,
|
||||
user=aime_id,
|
||||
player_name=player_name
|
||||
version=version, user=aime_id, player_name=player_name
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
player_name=sql.inserted.player_name
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(player_name=sql.inserted.player_name)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.error(
|
||||
f"{__name__} Failed to insert diva profile! aime id: {aime_id} username: {player_name}")
|
||||
f"{__name__} Failed to insert diva profile! aime id: {aime_id} username: {player_name}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
@@ -86,17 +91,17 @@ class DivaProfileData(BaseData):
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
self.logger.error(
|
||||
f"update_profile: failed to update profile! profile: {aime_id}")
|
||||
f"update_profile: failed to update profile! profile: {aime_id}"
|
||||
)
|
||||
return None
|
||||
|
||||
def get_profile(self, aime_id: int, version: int) -> Optional[List[Dict]]:
|
||||
"""
|
||||
Given a game version and either a profile or aime id, return the profile
|
||||
"""
|
||||
sql = profile.select(and_(
|
||||
profile.c.version == version,
|
||||
profile.c.user == aime_id
|
||||
))
|
||||
sql = profile.select(
|
||||
and_(profile.c.version == version, profile.c.user == aime_id)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
|
||||
@@ -10,27 +10,44 @@ pv_customize = Table(
|
||||
"diva_profile_pv_customize",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("pv_id", Integer, nullable=False),
|
||||
Column("mdl_eqp_ary", String(14), server_default="-999,-999,-999"),
|
||||
Column("c_itm_eqp_ary", String(59), server_default="-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999"),
|
||||
Column("ms_itm_flg_ary", String(59), server_default="-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"),
|
||||
Column(
|
||||
"c_itm_eqp_ary",
|
||||
String(59),
|
||||
server_default="-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999",
|
||||
),
|
||||
Column(
|
||||
"ms_itm_flg_ary",
|
||||
String(59),
|
||||
server_default="-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1",
|
||||
),
|
||||
Column("skin", Integer, server_default="-1"),
|
||||
Column("btn_se", Integer, server_default="-1"),
|
||||
Column("sld_se", Integer, server_default="-1"),
|
||||
Column("chsld_se", Integer, server_default="-1"),
|
||||
Column("sldtch_se", Integer, server_default="-1"),
|
||||
UniqueConstraint("user", "version", "pv_id", name="diva_profile_pv_customize_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class DivaPvCustomizeData(BaseData):
|
||||
def put_pv_customize(self, aime_id: int, version: int, pv_id: int,
|
||||
mdl_eqp_ary: str, c_itm_eqp_ary: str,
|
||||
ms_itm_flg_ary: str) -> Optional[int]:
|
||||
|
||||
def put_pv_customize(
|
||||
self,
|
||||
aime_id: int,
|
||||
version: int,
|
||||
pv_id: int,
|
||||
mdl_eqp_ary: str,
|
||||
c_itm_eqp_ary: str,
|
||||
ms_itm_flg_ary: str,
|
||||
) -> Optional[int]:
|
||||
sql = insert(pv_customize).values(
|
||||
version=version,
|
||||
user=aime_id,
|
||||
@@ -49,19 +66,19 @@ class DivaPvCustomizeData(BaseData):
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.error(f"{__name__} Failed to insert diva pv customize! aime id: {aime_id}")
|
||||
self.logger.error(
|
||||
f"{__name__} Failed to insert diva pv customize! aime id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_pv_customize(self, aime_id: int,
|
||||
pv_id: int) -> Optional[List[Dict]]:
|
||||
def get_pv_customize(self, aime_id: int, pv_id: int) -> Optional[List[Dict]]:
|
||||
"""
|
||||
Given either a profile or aime id, return a Pv Customize row
|
||||
"""
|
||||
sql = pv_customize.select(and_(
|
||||
pv_customize.c.user == aime_id,
|
||||
pv_customize.c.pv_id == pv_id
|
||||
))
|
||||
sql = pv_customize.select(
|
||||
and_(pv_customize.c.user == aime_id, pv_customize.c.pv_id == pv_id)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
|
||||
@@ -28,7 +28,7 @@ score = Table(
|
||||
Column("worst", Integer),
|
||||
Column("max_combo", Integer),
|
||||
UniqueConstraint("user", "pv_id", "difficulty", "edition", name="diva_score_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
playlog = Table(
|
||||
@@ -51,16 +51,29 @@ playlog = Table(
|
||||
Column("worst", Integer),
|
||||
Column("max_combo", Integer),
|
||||
Column("date_scored", TIMESTAMP, server_default=func.now()),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class DivaScoreData(BaseData):
|
||||
def put_best_score(self, user_id: int, game_version: int, song_id: int,
|
||||
difficulty: int, edition: int, song_score: int,
|
||||
atn_pnt: int, clr_kind: int, sort_kind: int,
|
||||
cool: int, fine: int, safe: int, sad: int,
|
||||
worst: int, max_combo: int) -> Optional[int]:
|
||||
def put_best_score(
|
||||
self,
|
||||
user_id: int,
|
||||
game_version: int,
|
||||
song_id: int,
|
||||
difficulty: int,
|
||||
edition: int,
|
||||
song_score: int,
|
||||
atn_pnt: int,
|
||||
clr_kind: int,
|
||||
sort_kind: int,
|
||||
cool: int,
|
||||
fine: int,
|
||||
safe: int,
|
||||
sad: int,
|
||||
worst: int,
|
||||
max_combo: int,
|
||||
) -> Optional[int]:
|
||||
"""
|
||||
Update the user's best score for a chart
|
||||
"""
|
||||
@@ -98,16 +111,30 @@ class DivaScoreData(BaseData):
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.error(
|
||||
f"{__name__} failed to insert best score! profile: {user_id}, song: {song_id}")
|
||||
f"{__name__} failed to insert best score! profile: {user_id}, song: {song_id}"
|
||||
)
|
||||
return None
|
||||
|
||||
return result.lastrowid
|
||||
|
||||
def put_playlog(self, user_id: int, game_version: int, song_id: int,
|
||||
difficulty: int, edition: int, song_score: int,
|
||||
atn_pnt: int, clr_kind: int, sort_kind: int,
|
||||
cool: int, fine: int, safe: int, sad: int,
|
||||
worst: int, max_combo: int) -> Optional[int]:
|
||||
def put_playlog(
|
||||
self,
|
||||
user_id: int,
|
||||
game_version: int,
|
||||
song_id: int,
|
||||
difficulty: int,
|
||||
edition: int,
|
||||
song_score: int,
|
||||
atn_pnt: int,
|
||||
clr_kind: int,
|
||||
sort_kind: int,
|
||||
cool: int,
|
||||
fine: int,
|
||||
safe: int,
|
||||
sad: int,
|
||||
worst: int,
|
||||
max_combo: int,
|
||||
) -> Optional[int]:
|
||||
"""
|
||||
Add an entry to the user's play log
|
||||
"""
|
||||
@@ -126,24 +153,28 @@ class DivaScoreData(BaseData):
|
||||
safe=safe,
|
||||
sad=sad,
|
||||
worst=worst,
|
||||
max_combo=max_combo
|
||||
max_combo=max_combo,
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
self.logger.error(
|
||||
f"{__name__} failed to insert playlog! profile: {user_id}, song: {song_id}, chart: {difficulty}")
|
||||
f"{__name__} failed to insert playlog! profile: {user_id}, song: {song_id}, chart: {difficulty}"
|
||||
)
|
||||
return None
|
||||
|
||||
return result.lastrowid
|
||||
|
||||
def get_best_user_score(self, user_id: int, pv_id: int, difficulty: int,
|
||||
edition: int) -> Optional[Dict]:
|
||||
def get_best_user_score(
|
||||
self, user_id: int, pv_id: int, difficulty: int, edition: int
|
||||
) -> Optional[Dict]:
|
||||
sql = score.select(
|
||||
and_(score.c.user == user_id,
|
||||
score.c.pv_id == pv_id,
|
||||
score.c.difficulty == difficulty,
|
||||
score.c.edition == edition)
|
||||
and_(
|
||||
score.c.user == user_id,
|
||||
score.c.pv_id == pv_id,
|
||||
score.c.difficulty == difficulty,
|
||||
score.c.edition == edition,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
@@ -151,36 +182,48 @@ class DivaScoreData(BaseData):
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_top3_scores(self, pv_id: int, difficulty: int,
|
||||
edition: int) -> Optional[List[Dict]]:
|
||||
sql = score.select(
|
||||
and_(score.c.pv_id == pv_id,
|
||||
score.c.difficulty == difficulty,
|
||||
score.c.edition == edition)
|
||||
).order_by(score.c.score.desc()).limit(3)
|
||||
def get_top3_scores(
|
||||
self, pv_id: int, difficulty: int, edition: int
|
||||
) -> Optional[List[Dict]]:
|
||||
sql = (
|
||||
score.select(
|
||||
and_(
|
||||
score.c.pv_id == pv_id,
|
||||
score.c.difficulty == difficulty,
|
||||
score.c.edition == edition,
|
||||
)
|
||||
)
|
||||
.order_by(score.c.score.desc())
|
||||
.limit(3)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_global_ranking(self, user_id: int, pv_id: int, difficulty: int,
|
||||
edition: int) -> Optional[List]:
|
||||
def get_global_ranking(
|
||||
self, user_id: int, pv_id: int, difficulty: int, edition: int
|
||||
) -> Optional[List]:
|
||||
# get the subquery max score of a user with pv_id, difficulty and
|
||||
# edition
|
||||
sql_sub = select([score.c.score]).filter(
|
||||
score.c.user == user_id,
|
||||
score.c.pv_id == pv_id,
|
||||
score.c.difficulty == difficulty,
|
||||
score.c.edition == edition
|
||||
).scalar_subquery()
|
||||
sql_sub = (
|
||||
select([score.c.score])
|
||||
.filter(
|
||||
score.c.user == user_id,
|
||||
score.c.pv_id == pv_id,
|
||||
score.c.difficulty == difficulty,
|
||||
score.c.edition == edition,
|
||||
)
|
||||
.scalar_subquery()
|
||||
)
|
||||
|
||||
# Perform the main query, also rename the resulting column to ranking
|
||||
sql = select(func.count(score.c.id).label("ranking")).filter(
|
||||
score.c.score >= sql_sub,
|
||||
score.c.pv_id == pv_id,
|
||||
score.c.difficulty == difficulty,
|
||||
score.c.edition == edition
|
||||
score.c.edition == edition,
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
|
||||
@@ -25,7 +25,7 @@ music = Table(
|
||||
Column("bpm", Integer),
|
||||
Column("date", String(255)),
|
||||
UniqueConstraint("version", "songId", "chartId", name="diva_static_music_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
quests = Table(
|
||||
@@ -43,9 +43,8 @@ quests = Table(
|
||||
Column("quest_order", Integer),
|
||||
Column("start_datetime", String(255)),
|
||||
Column("end_datetime", String(255)),
|
||||
|
||||
UniqueConstraint("version", "questId", name="diva_static_quests_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
shop = Table(
|
||||
@@ -62,7 +61,7 @@ shop = Table(
|
||||
Column("end_date", String(255)),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
UniqueConstraint("version", "shopId", name="diva_static_shop_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
items = Table(
|
||||
@@ -79,64 +78,91 @@ items = Table(
|
||||
Column("end_date", String(255)),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
UniqueConstraint("version", "itemId", name="diva_static_items_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class DivaStaticData(BaseData):
|
||||
def put_quests(self, version: int, questId: int, name: str, kind: int, unknown_0: int, unknown_1: int, unknown_2: int, quest_order: int, start_datetime: str, end_datetime: str) -> Optional[int]:
|
||||
def put_quests(
|
||||
self,
|
||||
version: int,
|
||||
questId: int,
|
||||
name: str,
|
||||
kind: int,
|
||||
unknown_0: int,
|
||||
unknown_1: int,
|
||||
unknown_2: int,
|
||||
quest_order: int,
|
||||
start_datetime: str,
|
||||
end_datetime: str,
|
||||
) -> Optional[int]:
|
||||
sql = insert(quests).values(
|
||||
version = version,
|
||||
questId = questId,
|
||||
name = name,
|
||||
kind = kind,
|
||||
unknown_0 = unknown_0,
|
||||
unknown_1 = unknown_1,
|
||||
unknown_2 = unknown_2,
|
||||
quest_order = quest_order,
|
||||
start_datetime = start_datetime,
|
||||
end_datetime = end_datetime
|
||||
version=version,
|
||||
questId=questId,
|
||||
name=name,
|
||||
kind=kind,
|
||||
unknown_0=unknown_0,
|
||||
unknown_1=unknown_1,
|
||||
unknown_2=unknown_2,
|
||||
quest_order=quest_order,
|
||||
start_datetime=start_datetime,
|
||||
end_datetime=end_datetime,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
name = name
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(name=name)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_enabled_quests(self, version: int) -> Optional[List[Row]]:
|
||||
sql = select(quests).where(and_(quests.c.version == version, quests.c.quest_enable == True))
|
||||
sql = select(quests).where(
|
||||
and_(quests.c.version == version, quests.c.quest_enable == True)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_shop(self, version: int, shopId: int, name: str, type: int, points: int, unknown_0: int, start_date: str, end_date: str) -> Optional[int]:
|
||||
def put_shop(
|
||||
self,
|
||||
version: int,
|
||||
shopId: int,
|
||||
name: str,
|
||||
type: int,
|
||||
points: int,
|
||||
unknown_0: int,
|
||||
start_date: str,
|
||||
end_date: str,
|
||||
) -> Optional[int]:
|
||||
sql = insert(shop).values(
|
||||
version = version,
|
||||
shopId = shopId,
|
||||
name = name,
|
||||
type = type,
|
||||
points = points,
|
||||
unknown_0 = unknown_0,
|
||||
start_date = start_date,
|
||||
end_date = end_date
|
||||
version=version,
|
||||
shopId=shopId,
|
||||
name=name,
|
||||
type=type,
|
||||
points=points,
|
||||
unknown_0=unknown_0,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
name = name
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(name=name)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_enabled_shop(self, version: int, shopId: int) -> Optional[Row]:
|
||||
sql = select(shop).where(and_(
|
||||
shop.c.version == version,
|
||||
shop.c.shopId == shopId,
|
||||
shop.c.enabled == True))
|
||||
sql = select(shop).where(
|
||||
and_(
|
||||
shop.c.version == version,
|
||||
shop.c.shopId == shopId,
|
||||
shop.c.enabled == True,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
@@ -144,40 +170,52 @@ class DivaStaticData(BaseData):
|
||||
return result.fetchone()
|
||||
|
||||
def get_enabled_shops(self, version: int) -> Optional[List[Row]]:
|
||||
sql = select(shop).where(and_(
|
||||
shop.c.version == version,
|
||||
shop.c.enabled == True))
|
||||
sql = select(shop).where(
|
||||
and_(shop.c.version == version, shop.c.enabled == True)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_items(self, version: int, itemId: int, name: str, type: int, points: int, unknown_0: int, start_date: str, end_date: str) -> Optional[int]:
|
||||
def put_items(
|
||||
self,
|
||||
version: int,
|
||||
itemId: int,
|
||||
name: str,
|
||||
type: int,
|
||||
points: int,
|
||||
unknown_0: int,
|
||||
start_date: str,
|
||||
end_date: str,
|
||||
) -> Optional[int]:
|
||||
sql = insert(items).values(
|
||||
version = version,
|
||||
itemId = itemId,
|
||||
name = name,
|
||||
type = type,
|
||||
points = points,
|
||||
unknown_0 = unknown_0,
|
||||
start_date = start_date,
|
||||
end_date = end_date
|
||||
version=version,
|
||||
itemId=itemId,
|
||||
name=name,
|
||||
type=type,
|
||||
points=points,
|
||||
unknown_0=unknown_0,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
name = name
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(name=name)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_enabled_item(self, version: int, itemId: int) -> Optional[Row]:
|
||||
sql = select(items).where(and_(
|
||||
items.c.version == version,
|
||||
items.c.itemId == itemId,
|
||||
items.c.enabled == True))
|
||||
sql = select(items).where(
|
||||
and_(
|
||||
items.c.version == version,
|
||||
items.c.itemId == itemId,
|
||||
items.c.enabled == True,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
@@ -185,66 +223,89 @@ class DivaStaticData(BaseData):
|
||||
return result.fetchone()
|
||||
|
||||
def get_enabled_items(self, version: int) -> Optional[List[Row]]:
|
||||
sql = select(items).where(and_(
|
||||
items.c.version == version,
|
||||
items.c.enabled == True))
|
||||
sql = select(items).where(
|
||||
and_(items.c.version == version, items.c.enabled == True)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_music(self, version: int, song: int, chart: int, title: str, arranger: str, illustrator: str,
|
||||
lyrics: str, music_comp: str, level: float, bpm: int, date: str) -> Optional[int]:
|
||||
def put_music(
|
||||
self,
|
||||
version: int,
|
||||
song: int,
|
||||
chart: int,
|
||||
title: str,
|
||||
arranger: str,
|
||||
illustrator: str,
|
||||
lyrics: str,
|
||||
music_comp: str,
|
||||
level: float,
|
||||
bpm: int,
|
||||
date: str,
|
||||
) -> Optional[int]:
|
||||
sql = insert(music).values(
|
||||
version = version,
|
||||
songId = song,
|
||||
chartId = chart,
|
||||
title = title,
|
||||
vocaloid_arranger = arranger,
|
||||
pv_illustrator = illustrator,
|
||||
lyrics = lyrics,
|
||||
bg_music = music_comp,
|
||||
level = level,
|
||||
bpm = bpm,
|
||||
date = date
|
||||
version=version,
|
||||
songId=song,
|
||||
chartId=chart,
|
||||
title=title,
|
||||
vocaloid_arranger=arranger,
|
||||
pv_illustrator=illustrator,
|
||||
lyrics=lyrics,
|
||||
bg_music=music_comp,
|
||||
level=level,
|
||||
bpm=bpm,
|
||||
date=date,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
title = title,
|
||||
vocaloid_arranger = arranger,
|
||||
pv_illustrator = illustrator,
|
||||
lyrics = lyrics,
|
||||
bg_music = music_comp,
|
||||
level = level,
|
||||
bpm = bpm,
|
||||
date = date
|
||||
title=title,
|
||||
vocaloid_arranger=arranger,
|
||||
pv_illustrator=illustrator,
|
||||
lyrics=lyrics,
|
||||
bg_music=music_comp,
|
||||
level=level,
|
||||
bpm=bpm,
|
||||
date=date,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_music(self, version: int, song_id: Optional[int] = None) -> Optional[List[Row]]:
|
||||
|
||||
def get_music(
|
||||
self, version: int, song_id: Optional[int] = None
|
||||
) -> Optional[List[Row]]:
|
||||
if song_id is None:
|
||||
sql = select(music).where(music.c.version == version)
|
||||
else:
|
||||
sql = select(music).where(and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
))
|
||||
sql = select(music).where(
|
||||
and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_music_chart(self, version: int, song_id: int, chart_id: int) -> Optional[List[Row]]:
|
||||
sql = select(music).where(and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
music.c.chartId == chart_id
|
||||
))
|
||||
|
||||
def get_music_chart(
|
||||
self, version: int, song_id: int, chart_id: int
|
||||
) -> Optional[List[Row]]:
|
||||
sql = select(music).where(
|
||||
and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
music.c.chartId == chart_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
@@ -6,13 +6,5 @@ from titles.mai2.read import Mai2Reader
|
||||
index = Mai2Servlet
|
||||
database = Mai2Data
|
||||
reader = Mai2Reader
|
||||
|
||||
use_default_title = True
|
||||
include_protocol = True
|
||||
title_secure = False
|
||||
game_codes = [Mai2Constants.GAME_CODE]
|
||||
trailing_slash = True
|
||||
use_default_host = False
|
||||
host = ""
|
||||
|
||||
current_schema_version = 1
|
||||
current_schema_version = 2
|
||||
|
||||
@@ -7,7 +7,8 @@ from titles.mai2.const import Mai2Constants
|
||||
from titles.mai2.config import Mai2Config
|
||||
from titles.mai2.database import Mai2Data
|
||||
|
||||
class Mai2Base():
|
||||
|
||||
class Mai2Base:
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
|
||||
self.core_config = cfg
|
||||
self.game_config = game_cfg
|
||||
@@ -17,23 +18,27 @@ class Mai2Base():
|
||||
self.logger = logging.getLogger("mai2")
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict):
|
||||
reboot_start = date.strftime(datetime.now() + timedelta(hours=3), Mai2Constants.DATE_TIME_FORMAT)
|
||||
reboot_end = date.strftime(datetime.now() + timedelta(hours=4), Mai2Constants.DATE_TIME_FORMAT)
|
||||
reboot_start = date.strftime(
|
||||
datetime.now() + timedelta(hours=3), Mai2Constants.DATE_TIME_FORMAT
|
||||
)
|
||||
reboot_end = date.strftime(
|
||||
datetime.now() + timedelta(hours=4), Mai2Constants.DATE_TIME_FORMAT
|
||||
)
|
||||
return {
|
||||
"gameSetting": {
|
||||
"isMaintenance": "false",
|
||||
"requestInterval": 10,
|
||||
"rebootStartTime": reboot_start,
|
||||
"rebootEndTime": reboot_end,
|
||||
"movieUploadLimit": 10000,
|
||||
"movieStatus": 0,
|
||||
"movieServerUri": "",
|
||||
"deliverServerUri": "",
|
||||
"oldServerUri": "",
|
||||
"usbDlServerUri": "",
|
||||
"rebootInterval": 0
|
||||
"gameSetting": {
|
||||
"isMaintenance": "false",
|
||||
"requestInterval": 10,
|
||||
"rebootStartTime": reboot_start,
|
||||
"rebootEndTime": reboot_end,
|
||||
"movieUploadLimit": 10000,
|
||||
"movieStatus": 0,
|
||||
"movieServerUri": "",
|
||||
"deliverServerUri": "",
|
||||
"oldServerUri": "",
|
||||
"usbDlServerUri": "",
|
||||
"rebootInterval": 0,
|
||||
},
|
||||
"isAouAccession": "true",
|
||||
"isAouAccession": "true",
|
||||
}
|
||||
|
||||
def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
|
||||
@@ -46,34 +51,44 @@ class Mai2Base():
|
||||
def handle_get_game_event_api_request(self, data: Dict) -> Dict:
|
||||
events = self.data.static.get_enabled_events(self.version)
|
||||
events_lst = []
|
||||
if events is None: return {"type": data["type"], "length": 0, "gameEventList": []}
|
||||
if events is None:
|
||||
return {"type": data["type"], "length": 0, "gameEventList": []}
|
||||
|
||||
for event in events:
|
||||
events_lst.append({
|
||||
"type": event["type"],
|
||||
"id": event["eventId"],
|
||||
"startDate": "2017-12-05 07:00:00.0",
|
||||
"endDate": "2099-12-31 00:00:00.0"
|
||||
})
|
||||
events_lst.append(
|
||||
{
|
||||
"type": event["type"],
|
||||
"id": event["eventId"],
|
||||
"startDate": "2017-12-05 07:00:00.0",
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
}
|
||||
)
|
||||
|
||||
return {"type": data["type"], "length": len(events_lst), "gameEventList": events_lst}
|
||||
return {
|
||||
"type": data["type"],
|
||||
"length": len(events_lst),
|
||||
"gameEventList": events_lst,
|
||||
}
|
||||
|
||||
def handle_get_game_ng_music_id_api_request(self, data: Dict) -> Dict:
|
||||
return {"length": 0, "musicIdList": []}
|
||||
|
||||
def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
|
||||
game_charge_list = self.data.static.get_enabled_tickets(self.version, 1)
|
||||
if game_charge_list is None: return {"length": 0, "gameChargeList": []}
|
||||
if game_charge_list is None:
|
||||
return {"length": 0, "gameChargeList": []}
|
||||
|
||||
charge_list = []
|
||||
for x in range(len(game_charge_list)):
|
||||
charge_list.append({
|
||||
"orderId": x,
|
||||
"chargeId": game_charge_list[x]["ticketId"],
|
||||
"price": game_charge_list[x]["price"],
|
||||
"startDate": "2017-12-05 07:00:00.0",
|
||||
"endDate": "2099-12-31 00:00:00.0"
|
||||
})
|
||||
charge_list.append(
|
||||
{
|
||||
"orderId": x,
|
||||
"chargeId": game_charge_list[x]["ticketId"],
|
||||
"price": game_charge_list[x]["price"],
|
||||
"startDate": "2017-12-05 07:00:00.0",
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
}
|
||||
)
|
||||
|
||||
return {"length": len(charge_list), "gameChargeList": charge_list}
|
||||
|
||||
@@ -92,7 +107,8 @@ class Mai2Base():
|
||||
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
|
||||
if p is None or o is None:
|
||||
return {} # Register
|
||||
profile = p._asdict()
|
||||
option = o._asdict()
|
||||
|
||||
@@ -106,20 +122,24 @@ class Mai2Base():
|
||||
"lastLoginDate": profile["lastLoginDate"],
|
||||
"lastPlayDate": profile["lastPlayDate"],
|
||||
"playerRating": profile["playerRating"],
|
||||
"nameplateId": 0, # Unused
|
||||
"nameplateId": 0, # Unused
|
||||
"iconId": profile["iconId"],
|
||||
"trophyId": 0, # Unused
|
||||
"trophyId": 0, # Unused
|
||||
"partnerId": profile["partnerId"],
|
||||
"frameId": profile["frameId"],
|
||||
"dispRate": option["dispRate"], # 0: all/begin, 1: disprate, 2: dispDan, 3: hide, 4: end
|
||||
"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+
|
||||
"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)
|
||||
|
||||
@@ -137,10 +157,10 @@ class Mai2Base():
|
||||
"returnCode": 1,
|
||||
"lastLoginDate": lastLoginDate,
|
||||
"loginCount": loginCt,
|
||||
"consecutiveLoginCount": 0, # We don't really have a way to track this...
|
||||
"loginId": loginCt # Used with the playlog!
|
||||
"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"]
|
||||
@@ -154,102 +174,129 @@ class Mai2Base():
|
||||
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])
|
||||
|
||||
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])
|
||||
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])
|
||||
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])
|
||||
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 k, v in upsert["userActivityList"][0].items():
|
||||
for act in v:
|
||||
self.data.profile.put_profile_activity(user_id, act)
|
||||
|
||||
if upsert["isNewCharacterList"] and int(upsert["isNewCharacterList"]) > 0:
|
||||
for char in upsert["userCharacterList"]:
|
||||
self.data.item.put_character(user_id, char["characterId"], char["level"], char["awakening"], char["useCount"])
|
||||
self.data.item.put_character(
|
||||
user_id,
|
||||
char["characterId"],
|
||||
char["level"],
|
||||
char["awakening"],
|
||||
char["useCount"],
|
||||
)
|
||||
|
||||
if upsert["isNewItemList"] and int(upsert["isNewItemList"]) > 0:
|
||||
for item in upsert["userItemList"]:
|
||||
self.data.item.put_item(user_id, int(item["itemKind"]), item["itemId"], item["stock"], item["isValid"])
|
||||
self.data.item.put_item(
|
||||
user_id,
|
||||
int(item["itemKind"]),
|
||||
item["itemId"],
|
||||
item["stock"],
|
||||
item["isValid"],
|
||||
)
|
||||
|
||||
if upsert["isNewLoginBonusList"] and int(upsert["isNewLoginBonusList"]) > 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"])
|
||||
self.data.item.put_login_bonus(
|
||||
user_id,
|
||||
login_bonus["bonusId"],
|
||||
login_bonus["point"],
|
||||
login_bonus["isCurrent"],
|
||||
login_bonus["isComplete"],
|
||||
)
|
||||
|
||||
if upsert["isNewMapList"] and int(upsert["isNewMapList"]) > 0:
|
||||
for map in upsert["userMapList"]:
|
||||
self.data.item.put_map(user_id, map["mapId"], map["distance"], map["isLock"], map["isClear"], map["isComplete"])
|
||||
|
||||
self.data.item.put_map(
|
||||
user_id,
|
||||
map["mapId"],
|
||||
map["distance"],
|
||||
map["isLock"],
|
||||
map["isClear"],
|
||||
map["isComplete"],
|
||||
)
|
||||
|
||||
if upsert["isNewMusicDetailList"] and int(upsert["isNewMusicDetailList"]) > 0:
|
||||
for music in upsert["userMusicDetailList"]:
|
||||
self.data.score.put_best_score(user_id, music)
|
||||
|
||||
|
||||
if upsert["isNewCourseList"] and int(upsert["isNewCourseList"]) > 0:
|
||||
for course in upsert["userCourseList"]:
|
||||
self.data.score.put_course(user_id, course)
|
||||
|
||||
|
||||
if upsert["isNewFavoriteList"] and int(upsert["isNewFavoriteList"]) > 0:
|
||||
for fav in upsert["userFavoriteList"]:
|
||||
self.data.item.put_favorite(user_id, fav["kind"], fav["itemIdList"])
|
||||
|
||||
if "isNewFriendSeasonRankingList" in upsert and int(upsert["isNewFriendSeasonRankingList"]) > 0:
|
||||
for fsr in upsert["userFriendSeasonRankingList"]:
|
||||
pass
|
||||
# if "isNewFriendSeasonRankingList" in upsert and int(upsert["isNewFriendSeasonRankingList"]) > 0:
|
||||
# for fsr in upsert["userFriendSeasonRankingList"]:
|
||||
# pass
|
||||
|
||||
def handle_user_logout_api_request(self, data: Dict) -> Dict:
|
||||
pass
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
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
|
||||
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
|
||||
}
|
||||
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
|
||||
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
|
||||
}
|
||||
return {"userId": data["userId"], "userOption": options_dict}
|
||||
|
||||
def handle_get_user_card_api_request(self, data: Dict) -> Dict:
|
||||
return {"userId": data["userId"], "nextIndex": 0, "userCardList": []}
|
||||
@@ -266,73 +313,83 @@ class Mai2Base():
|
||||
|
||||
for x in range(next_idx, data["maxCount"]):
|
||||
try:
|
||||
user_item_list.append({"item_kind": user_items[x]["item_kind"], "item_id": user_items[x]["item_id"],
|
||||
"stock": user_items[x]["stock"], "isValid": user_items[x]["is_valid"]})
|
||||
except: break
|
||||
|
||||
user_item_list.append(
|
||||
{
|
||||
"item_kind": user_items[x]["item_kind"],
|
||||
"item_id": user_items[x]["item_id"],
|
||||
"stock": user_items[x]["stock"],
|
||||
"isValid": user_items[x]["is_valid"],
|
||||
}
|
||||
)
|
||||
except:
|
||||
break
|
||||
|
||||
if len(user_item_list) == data["maxCount"]:
|
||||
next_idx = data["nextIndex"] + data["maxCount"] + 1
|
||||
break
|
||||
|
||||
return {"userId": data["userId"], "nextIndex": next_idx, "itemKind": kind, "userItemList": user_item_list}
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"nextIndex": next_idx,
|
||||
"itemKind": kind,
|
||||
"userItemList": user_item_list,
|
||||
}
|
||||
|
||||
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:
|
||||
chara_list.append({
|
||||
"characterId": chara["character_id"],
|
||||
"level": chara["level"],
|
||||
"awakening": chara["awakening"],
|
||||
"useCount": chara["use_count"],
|
||||
})
|
||||
chara_list.append(
|
||||
{
|
||||
"characterId": chara["character_id"],
|
||||
"level": chara["level"],
|
||||
"awakening": chara["awakening"],
|
||||
"useCount": chara["use_count"],
|
||||
}
|
||||
)
|
||||
|
||||
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
|
||||
if favorites is None:
|
||||
return
|
||||
|
||||
userFavs = []
|
||||
for fav in favorites:
|
||||
userFavs.append({
|
||||
"userId": data["userId"],
|
||||
"itemKind": fav["itemKind"],
|
||||
"itemIdList": fav["itemIdList"]
|
||||
})
|
||||
userFavs.append(
|
||||
{
|
||||
"userId": data["userId"],
|
||||
"itemKind": fav["itemKind"],
|
||||
"itemIdList": fav["itemIdList"],
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userFavoriteData": userFavs
|
||||
}
|
||||
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
|
||||
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
|
||||
}
|
||||
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
|
||||
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
|
||||
}
|
||||
return {"userId": data["userId"], "userRating": rating_dict}
|
||||
|
||||
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
@@ -340,31 +397,27 @@ class Mai2Base():
|
||||
"""
|
||||
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
|
||||
if playlist is None or musiclist is None:
|
||||
return
|
||||
|
||||
plst = []
|
||||
mlst = []
|
||||
|
||||
for play in playlist:
|
||||
tmp = play._asdict()
|
||||
tmp = play._asdict()
|
||||
tmp["id"] = tmp["activityId"]
|
||||
tmp.pop("activityId")
|
||||
tmp.pop("user")
|
||||
plst.append(tmp)
|
||||
|
||||
for music in musiclist:
|
||||
tmp = music._asdict()
|
||||
tmp = music._asdict()
|
||||
tmp["id"] = tmp["activityId"]
|
||||
tmp.pop("activityId")
|
||||
tmp.pop("user")
|
||||
mlst.append(tmp)
|
||||
|
||||
return {
|
||||
"userActivity": {
|
||||
"playList": plst,
|
||||
"musicList": mlst
|
||||
}
|
||||
}
|
||||
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"])
|
||||
@@ -389,21 +442,30 @@ class Mai2Base():
|
||||
|
||||
for x in range(data["nextIndex"], data["maxCount"] + data["nextIndex"]):
|
||||
try:
|
||||
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.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"],
|
||||
}
|
||||
)
|
||||
except:
|
||||
break
|
||||
|
||||
# We're capped and still have some left to go
|
||||
if len(friend_season_ranking_list) == data["maxCount"] and len(friend_season_ranking) > data["maxCount"] + data["nextIndex"]:
|
||||
if (
|
||||
len(friend_season_ranking_list) == data["maxCount"]
|
||||
and len(friend_season_ranking) > data["maxCount"] + data["nextIndex"]
|
||||
):
|
||||
next_index = data["maxCount"] + data["nextIndex"]
|
||||
|
||||
return {"userId": data["userId"], "nextIndex": next_index, "userFriendSeasonRankingList": friend_season_ranking_list}
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"nextIndex": next_index,
|
||||
"userFriendSeasonRankingList": friend_season_ranking_list,
|
||||
}
|
||||
|
||||
def handle_get_user_map_api_request(self, data: Dict) -> Dict:
|
||||
maps = self.data.item.get_maps(data["userId"])
|
||||
@@ -412,21 +474,30 @@ class Mai2Base():
|
||||
|
||||
for x in range(data["nextIndex"], data["maxCount"] + data["nextIndex"]):
|
||||
try:
|
||||
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"],
|
||||
})
|
||||
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:
|
||||
break
|
||||
|
||||
# We're capped and still have some left to go
|
||||
if len(map_list) == data["maxCount"] and len(maps) > data["maxCount"] + data["nextIndex"]:
|
||||
if (
|
||||
len(map_list) == data["maxCount"]
|
||||
and len(maps) > data["maxCount"] + data["nextIndex"]
|
||||
):
|
||||
next_index = data["maxCount"] + data["nextIndex"]
|
||||
|
||||
return {"userId": data["userId"], "nextIndex": next_index, "userMapList": map_list}
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"nextIndex": next_index,
|
||||
"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"])
|
||||
@@ -435,20 +506,29 @@ class Mai2Base():
|
||||
|
||||
for x in range(data["nextIndex"], data["maxCount"] + data["nextIndex"]):
|
||||
try:
|
||||
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"],
|
||||
})
|
||||
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:
|
||||
break
|
||||
|
||||
# We're capped and still have some left to go
|
||||
if len(login_bonus_list) == data["maxCount"] and len(login_bonuses) > data["maxCount"] + data["nextIndex"]:
|
||||
if (
|
||||
len(login_bonus_list) == data["maxCount"]
|
||||
and len(login_bonuses) > data["maxCount"] + data["nextIndex"]
|
||||
):
|
||||
next_index = data["maxCount"] + data["nextIndex"]
|
||||
|
||||
return {"userId": data["userId"], "nextIndex": next_index, "userLoginBonusList": login_bonus_list}
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"nextIndex": next_index,
|
||||
"userLoginBonusList": login_bonus_list,
|
||||
}
|
||||
|
||||
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
|
||||
return {"userId": data["userId"], "length": 0, "userRegionList": []}
|
||||
@@ -460,18 +540,24 @@ class Mai2Base():
|
||||
|
||||
if songs is not None:
|
||||
for song in songs:
|
||||
music_detail_list.append({
|
||||
"musicId": song["song_id"],
|
||||
"level": song["chart_id"],
|
||||
"playCount": song["play_count"],
|
||||
"achievement": song["achievement"],
|
||||
"comboStatus": song["combo_status"],
|
||||
"syncStatus": song["sync_status"],
|
||||
"deluxscoreMax": song["dx_score"],
|
||||
"scoreRank": song["score_rank"],
|
||||
})
|
||||
music_detail_list.append(
|
||||
{
|
||||
"musicId": song["song_id"],
|
||||
"level": song["chart_id"],
|
||||
"playCount": song["play_count"],
|
||||
"achievement": song["achievement"],
|
||||
"comboStatus": song["combo_status"],
|
||||
"syncStatus": song["sync_status"],
|
||||
"deluxscoreMax": song["dx_score"],
|
||||
"scoreRank": song["score_rank"],
|
||||
}
|
||||
)
|
||||
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}]}
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"nextIndex": next_index,
|
||||
"userMusicList": [{"userMusicDetailList": music_detail_list}],
|
||||
}
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
from core.config import CoreConfig
|
||||
|
||||
class Mai2ServerConfig():
|
||||
|
||||
class Mai2ServerConfig:
|
||||
def __init__(self, parent: "Mai2Config") -> None:
|
||||
self.__config = parent
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(self.__config, 'mai2', 'server', 'enable', default=True)
|
||||
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "mai2", "server", "enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def loglevel(self) -> int:
|
||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'mai2', 'server', 'loglevel', default="info"))
|
||||
return CoreConfig.str_to_loglevel(
|
||||
CoreConfig.get_config_field(
|
||||
self.__config, "mai2", "server", "loglevel", default="info"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Mai2Config(dict):
|
||||
def __init__(self) -> None:
|
||||
self.server = Mai2ServerConfig(self)
|
||||
self.server = Mai2ServerConfig(self)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class Mai2Constants():
|
||||
class Mai2Constants:
|
||||
GRADE = {
|
||||
"D": 0,
|
||||
"C": 1,
|
||||
@@ -13,22 +13,10 @@ class Mai2Constants():
|
||||
"SS": 10,
|
||||
"SS+": 11,
|
||||
"SSS": 12,
|
||||
"SSS+": 13
|
||||
}
|
||||
FC = {
|
||||
"None": 0,
|
||||
"FC": 1,
|
||||
"FC+": 2,
|
||||
"AP": 3,
|
||||
"AP+": 4
|
||||
}
|
||||
SYNC = {
|
||||
"None": 0,
|
||||
"FS": 1,
|
||||
"FS+": 2,
|
||||
"FDX": 3,
|
||||
"FDX+": 4
|
||||
"SSS+": 13,
|
||||
}
|
||||
FC = {"None": 0, "FC": 1, "FC+": 2, "AP": 3, "AP+": 4}
|
||||
SYNC = {"None": 0, "FS": 1, "FS+": 2, "FDX": 3, "FDX+": 4}
|
||||
|
||||
DATE_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
@@ -43,9 +31,15 @@ class Mai2Constants():
|
||||
VER_MAIMAI_DX_UNIVERSE = 4
|
||||
VER_MAIMAI_DX_UNIVERSE_PLUS = 5
|
||||
|
||||
VERSION_STRING = ("maimai Delux", "maimai Delux PLUS", "maimai Delux Splash", "maimai Delux Splash PLUS", "maimai Delux Universe",
|
||||
"maimai Delux Universe PLUS")
|
||||
VERSION_STRING = (
|
||||
"maimai Delux",
|
||||
"maimai Delux PLUS",
|
||||
"maimai Delux Splash",
|
||||
"maimai Delux Splash PLUS",
|
||||
"maimai Delux Universe",
|
||||
"maimai Delux Universe PLUS",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def game_ver_to_string(cls, ver: int):
|
||||
return cls.VERSION_STRING[ver]
|
||||
return cls.VERSION_STRING[ver]
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
from core.data import Data
|
||||
from core.config import CoreConfig
|
||||
from titles.mai2.schema import Mai2ItemData, Mai2ProfileData, Mai2StaticData, Mai2ScoreData
|
||||
from titles.mai2.schema import (
|
||||
Mai2ItemData,
|
||||
Mai2ProfileData,
|
||||
Mai2StaticData,
|
||||
Mai2ScoreData,
|
||||
)
|
||||
|
||||
|
||||
class Mai2Data(Data):
|
||||
def __init__(self, cfg: CoreConfig) -> None:
|
||||
@@ -9,4 +15,4 @@ class Mai2Data(Data):
|
||||
self.profile = Mai2ProfileData(self.config, self.session)
|
||||
self.item = Mai2ItemData(self.config, self.session)
|
||||
self.static = Mai2StaticData(self.config, self.session)
|
||||
self.score = Mai2ScoreData(self.config, self.session)
|
||||
self.score = Mai2ScoreData(self.config, self.session)
|
||||
|
||||
@@ -6,6 +6,8 @@ import string
|
||||
import logging, coloredlogs
|
||||
import zlib
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from os import path
|
||||
from typing import Tuple
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.mai2.config import Mai2Config
|
||||
@@ -18,11 +20,14 @@ from titles.mai2.universe import Mai2Universe
|
||||
from titles.mai2.universeplus import Mai2UniversePlus
|
||||
|
||||
|
||||
class Mai2Servlet():
|
||||
class Mai2Servlet:
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.game_cfg = Mai2Config()
|
||||
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/{Mai2Constants.CONFIG_NAME}")))
|
||||
if path.exists(f"{cfg_dir}/{Mai2Constants.CONFIG_NAME}"):
|
||||
self.game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{Mai2Constants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
self.versions = [
|
||||
Mai2Base(core_cfg, self.game_cfg),
|
||||
@@ -36,74 +41,112 @@ class Mai2Servlet():
|
||||
self.logger = logging.getLogger("mai2")
|
||||
log_fmt_str = "[%(asctime)s] Mai2 | %(levelname)s | %(message)s"
|
||||
log_fmt = logging.Formatter(log_fmt_str)
|
||||
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "mai2"), encoding='utf8',
|
||||
when="d", backupCount=10)
|
||||
fileHandler = TimedRotatingFileHandler(
|
||||
"{0}/{1}.log".format(self.core_cfg.server.log_dir, "mai2"),
|
||||
encoding="utf8",
|
||||
when="d",
|
||||
backupCount=10,
|
||||
)
|
||||
|
||||
fileHandler.setFormatter(log_fmt)
|
||||
|
||||
|
||||
consoleHandler = logging.StreamHandler()
|
||||
consoleHandler.setFormatter(log_fmt)
|
||||
|
||||
self.logger.addHandler(fileHandler)
|
||||
self.logger.addHandler(consoleHandler)
|
||||
|
||||
|
||||
self.logger.setLevel(self.game_cfg.server.loglevel)
|
||||
coloredlogs.install(level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str)
|
||||
coloredlogs.install(
|
||||
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_allnet_info(
|
||||
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
|
||||
) -> Tuple[bool, str, str]:
|
||||
game_cfg = Mai2Config()
|
||||
|
||||
if path.exists(f"{cfg_dir}/{Mai2Constants.CONFIG_NAME}"):
|
||||
game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{Mai2Constants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
if not game_cfg.server.enable:
|
||||
return (False, "", "")
|
||||
|
||||
if core_cfg.server.is_develop:
|
||||
return (
|
||||
True,
|
||||
f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/",
|
||||
f"{core_cfg.title.hostname}:{core_cfg.title.port}/",
|
||||
)
|
||||
|
||||
return (
|
||||
True,
|
||||
f"http://{core_cfg.title.hostname}/{game_code}/$v/",
|
||||
f"{core_cfg.title.hostname}/",
|
||||
)
|
||||
|
||||
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
|
||||
if url_path.lower() == "/ping":
|
||||
return zlib.compress(b'{"returnCode": "1"}')
|
||||
|
||||
req_raw = request.content.getvalue()
|
||||
url = request.uri.decode()
|
||||
url_split = url_path.split("/")
|
||||
internal_ver = 0
|
||||
endpoint = url_split[len(url_split) - 1]
|
||||
|
||||
if version < 105: # 1.0
|
||||
if version < 105: # 1.0
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX
|
||||
elif version >= 105 and version < 110: # Plus
|
||||
elif version >= 105 and version < 110: # Plus
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_PLUS
|
||||
elif version >= 110 and version < 115: # Splash
|
||||
elif version >= 110 and version < 115: # Splash
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_SPLASH
|
||||
elif version >= 115 and version < 120: # Splash Plus
|
||||
elif version >= 115 and version < 120: # 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
|
||||
elif version >= 125: # Universe Plus
|
||||
elif version >= 125: # Universe Plus
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
|
||||
|
||||
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
|
||||
# doing encrypted. The likelyhood of false positives is low but
|
||||
# If we get a 32 character long hex string, it's a hash and we're
|
||||
# doing encrypted. The likelyhood of false positives is low but
|
||||
# technically not 0
|
||||
self.logger.error("Encryption not supported at this time")
|
||||
|
||||
try:
|
||||
try:
|
||||
unzip = zlib.decompress(req_raw)
|
||||
|
||||
|
||||
except zlib.error as e:
|
||||
self.logger.error(f"Failed to decompress v{version} {endpoint} request -> {e}")
|
||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||
|
||||
self.logger.error(
|
||||
f"Failed to decompress v{version} {endpoint} request -> {e}"
|
||||
)
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
|
||||
req_data = json.loads(unzip)
|
||||
|
||||
|
||||
self.logger.info(f"v{version} {endpoint} request - {req_data}")
|
||||
|
||||
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
||||
|
||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||
return zlib.compress(b'{"returnCode": 1}')
|
||||
|
||||
try:
|
||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||
resp = handler(req_data)
|
||||
|
||||
except AttributeError as e:
|
||||
self.logger.warning(f"Unhandled v{version} request {endpoint} - {e}")
|
||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
|
||||
if resp == None:
|
||||
resp = {'returnCode': 1}
|
||||
|
||||
resp = {"returnCode": 1}
|
||||
|
||||
self.logger.info(f"Response {resp}")
|
||||
|
||||
|
||||
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||
|
||||
@@ -8,7 +8,8 @@ from titles.mai2.base import Mai2Base
|
||||
from titles.mai2.config import Mai2Config
|
||||
from titles.mai2.const import Mai2Constants
|
||||
|
||||
|
||||
class Mai2Plus(Mai2Base):
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_PLUS
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_PLUS
|
||||
|
||||
@@ -11,25 +11,35 @@ from read import BaseReader
|
||||
from titles.mai2.const import Mai2Constants
|
||||
from titles.mai2.database import Mai2Data
|
||||
|
||||
|
||||
class Mai2Reader(BaseReader):
|
||||
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str], opt_dir: Optional[str], extra: Optional[str]) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
config: CoreConfig,
|
||||
version: int,
|
||||
bin_dir: Optional[str],
|
||||
opt_dir: Optional[str],
|
||||
extra: Optional[str],
|
||||
) -> None:
|
||||
super().__init__(config, version, bin_dir, opt_dir, extra)
|
||||
self.data = Mai2Data(config)
|
||||
|
||||
try:
|
||||
self.logger.info(f"Start importer for {Mai2Constants.game_ver_to_string(version)}")
|
||||
self.logger.info(
|
||||
f"Start importer for {Mai2Constants.game_ver_to_string(version)}"
|
||||
)
|
||||
except IndexError:
|
||||
self.logger.error(f"Invalid maidx version {version}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def read(self) -> None:
|
||||
data_dirs = []
|
||||
if self.bin_dir is not None:
|
||||
data_dirs += self.get_data_directories(self.bin_dir)
|
||||
|
||||
|
||||
if self.opt_dir is not None:
|
||||
data_dirs += self.get_data_directories(self.opt_dir)
|
||||
|
||||
data_dirs += self.get_data_directories(self.opt_dir)
|
||||
|
||||
for dir in data_dirs:
|
||||
self.logger.info(f"Read from {dir}")
|
||||
self.get_events(f"{dir}/event")
|
||||
@@ -43,47 +53,64 @@ class Mai2Reader(BaseReader):
|
||||
for dir in dirs:
|
||||
if os.path.exists(f"{root}/{dir}/Event.xml"):
|
||||
with open(f"{root}/{dir}/Event.xml", encoding="utf-8") as f:
|
||||
|
||||
troot = ET.fromstring(f.read())
|
||||
|
||||
name = troot.find('name').find('str').text
|
||||
id = int(troot.find('name').find('id').text)
|
||||
event_type = int(troot.find('infoType').text)
|
||||
name = troot.find("name").find("str").text
|
||||
id = int(troot.find("name").find("id").text)
|
||||
event_type = int(troot.find("infoType").text)
|
||||
|
||||
self.data.static.put_game_event(self.version, event_type, id, name)
|
||||
self.data.static.put_game_event(
|
||||
self.version, event_type, id, name
|
||||
)
|
||||
self.logger.info(f"Added event {id}...")
|
||||
|
||||
|
||||
def read_music(self, base_dir: str) -> None:
|
||||
self.logger.info(f"Reading music from {base_dir}...")
|
||||
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
for dir in dirs:
|
||||
for dir in dirs:
|
||||
if os.path.exists(f"{root}/{dir}/Music.xml"):
|
||||
with open(f"{root}/{dir}/Music.xml", encoding="utf-8") as f:
|
||||
troot = ET.fromstring(f.read())
|
||||
|
||||
song_id = int(troot.find('name').find('id').text)
|
||||
title = troot.find('name').find('str').text
|
||||
artist = troot.find('artistName').find('str').text
|
||||
genre = troot.find('genreName').find('str').text
|
||||
bpm = int(troot.find('bpm').text)
|
||||
added_ver = troot.find('AddVersion').find('str').text
|
||||
song_id = int(troot.find("name").find("id").text)
|
||||
title = troot.find("name").find("str").text
|
||||
artist = troot.find("artistName").find("str").text
|
||||
genre = troot.find("genreName").find("str").text
|
||||
bpm = int(troot.find("bpm").text)
|
||||
added_ver = troot.find("AddVersion").find("str").text
|
||||
|
||||
note_data = troot.find('notesData').findall('Notes')
|
||||
note_data = troot.find("notesData").findall("Notes")
|
||||
|
||||
for dif in note_data:
|
||||
path = dif.find('file').find('path').text
|
||||
path = dif.find("file").find("path").text
|
||||
if path is not None:
|
||||
if os.path.exists(f"{root}/{dir}/{path}"):
|
||||
chart_id = int(path.split(".")[0].split('_')[1])
|
||||
diff_num = float(f"{dif.find('level').text}.{dif.find('levelDecimal').text}")
|
||||
note_designer = dif.find('notesDesigner').find('str').text
|
||||
chart_id = int(path.split(".")[0].split("_")[1])
|
||||
diff_num = float(
|
||||
f"{dif.find('level').text}.{dif.find('levelDecimal').text}"
|
||||
)
|
||||
note_designer = (
|
||||
dif.find("notesDesigner").find("str").text
|
||||
)
|
||||
|
||||
self.data.static.put_game_music(
|
||||
self.version,
|
||||
song_id,
|
||||
chart_id,
|
||||
title,
|
||||
artist,
|
||||
genre,
|
||||
bpm,
|
||||
added_ver,
|
||||
diff_num,
|
||||
note_designer,
|
||||
)
|
||||
|
||||
self.logger.info(
|
||||
f"Added music id {song_id} chart {chart_id}"
|
||||
)
|
||||
|
||||
self.data.static.put_game_music(self.version, song_id, chart_id, title, artist,
|
||||
genre, bpm, added_ver, diff_num, note_designer)
|
||||
|
||||
self.logger.info(f"Added music id {song_id} chart {chart_id}")
|
||||
|
||||
def read_tickets(self, base_dir: str) -> None:
|
||||
self.logger.info(f"Reading tickets from {base_dir}...")
|
||||
|
||||
@@ -91,13 +118,14 @@ class Mai2Reader(BaseReader):
|
||||
for dir in dirs:
|
||||
if os.path.exists(f"{root}/{dir}/Ticket.xml"):
|
||||
with open(f"{root}/{dir}/Ticket.xml", encoding="utf-8") as f:
|
||||
|
||||
troot = ET.fromstring(f.read())
|
||||
|
||||
name = troot.find('name').find('str').text
|
||||
id = int(troot.find('name').find('id').text)
|
||||
ticket_type = int(troot.find('ticketKind').find('id').text)
|
||||
price = int(troot.find('creditNum').text)
|
||||
name = troot.find("name").find("str").text
|
||||
id = int(troot.find("name").find("id").text)
|
||||
ticket_type = int(troot.find("ticketKind").find("id").text)
|
||||
price = int(troot.find("creditNum").text)
|
||||
|
||||
self.data.static.put_game_ticket(self.version, id, ticket_type, price, name)
|
||||
self.data.static.put_game_ticket(
|
||||
self.version, id, ticket_type, price, name
|
||||
)
|
||||
self.logger.info(f"Added ticket {id}...")
|
||||
|
||||
@@ -3,4 +3,4 @@ from titles.mai2.schema.item import Mai2ItemData
|
||||
from titles.mai2.schema.static import Mai2StaticData
|
||||
from titles.mai2.schema.score import Mai2ScoreData
|
||||
|
||||
__all__ = [Mai2ProfileData, Mai2ItemData, Mai2StaticData, Mai2ScoreData]
|
||||
__all__ = [Mai2ProfileData, Mai2ItemData, Mai2StaticData, Mai2ScoreData]
|
||||
|
||||
@@ -12,20 +12,28 @@ character = Table(
|
||||
"mai2_item_character",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("character_id", Integer, nullable=False),
|
||||
Column("level", Integer, nullable=False, server_default="1"),
|
||||
Column("awakening", Integer, nullable=False, server_default="0"),
|
||||
Column("use_count", Integer, nullable=False, server_default="0"),
|
||||
UniqueConstraint("user", "character_id", name="mai2_item_character_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
card = Table(
|
||||
"mai2_item_card",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("card_kind", Integer, nullable=False),
|
||||
Column("card_id", Integer, nullable=False),
|
||||
Column("chara_id", Integer, nullable=False),
|
||||
@@ -33,54 +41,70 @@ card = Table(
|
||||
Column("start_date", String(255), nullable=False),
|
||||
Column("end_date", String(255), nullable=False),
|
||||
UniqueConstraint("user", "card_kind", "card_id", name="mai2_item_card_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
item = Table(
|
||||
"mai2_item_item",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("item_kind", Integer, nullable=False),
|
||||
Column("item_id", Integer, nullable=False),
|
||||
Column("stock", Integer, nullable=False, server_default="1"),
|
||||
Column("is_valid", Boolean, nullable=False, server_default="1"),
|
||||
UniqueConstraint("user", "item_kind", "item_id", name="mai2_item_item_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
map = Table(
|
||||
"mai2_item_map",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("map_id", Integer, nullable=False),
|
||||
Column("distance", Integer, nullable=False),
|
||||
Column("is_lock", Boolean, nullable=False, server_default="0"),
|
||||
Column("is_clear", Boolean, nullable=False, server_default="0"),
|
||||
Column("is_complete", Boolean, nullable=False, server_default="0"),
|
||||
UniqueConstraint("user", "map_id", name="mai2_item_map_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
login_bonus = Table(
|
||||
"mai2_item_login_bonus",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("bonus_id", Integer, nullable=False),
|
||||
Column("point", Integer, nullable=False),
|
||||
Column("is_current", Boolean, nullable=False, server_default="0"),
|
||||
Column("is_complete", Boolean, nullable=False, server_default="0"),
|
||||
UniqueConstraint("user", "bonus_id", name="mai2_item_login_bonus_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
friend_season_ranking = Table(
|
||||
"mai2_item_friend_season_ranking",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("season_id", Integer, nullable=False),
|
||||
Column("point", Integer, nullable=False),
|
||||
Column("rank", Integer, nullable=False),
|
||||
@@ -88,35 +112,46 @@ friend_season_ranking = Table(
|
||||
Column("user_name", String(8), nullable=False),
|
||||
Column("record_date", String(255), nullable=False),
|
||||
UniqueConstraint("user", "season_id", "user_name", name="mai2_item_login_bonus_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
favorite = Table(
|
||||
"mai2_item_favorite",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("itemKind", Integer, nullable=False),
|
||||
Column("itemIdList", JSON),
|
||||
UniqueConstraint("user", "itemKind", name="mai2_item_favorite_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
charge = Table(
|
||||
"mai2_item_charge",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("charge_id", Integer, nullable=False),
|
||||
Column("stock", Integer, nullable=False),
|
||||
Column("purchase_date", String(255), nullable=False),
|
||||
Column("valid_date", String(255), nullable=False),
|
||||
UniqueConstraint("user", "charge_id", name="mai2_item_charge_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class Mai2ItemData(BaseData):
|
||||
def put_item(self, user_id: int, item_kind: int, item_id: int, stock: int, is_valid: bool) -> None:
|
||||
def put_item(
|
||||
self, user_id: int, item_kind: int, item_id: int, stock: int, is_valid: bool
|
||||
) -> None:
|
||||
sql = insert(item).values(
|
||||
user=user_id,
|
||||
item_kind=item_kind,
|
||||
@@ -132,28 +167,47 @@ class Mai2ItemData(BaseData):
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_item: failed to insert item! user_id: {user_id}, item_kind: {item_kind}, item_id: {item_id}")
|
||||
self.logger.warn(
|
||||
f"put_item: failed to insert item! user_id: {user_id}, item_kind: {item_kind}, item_id: {item_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_items(self, user_id: int, item_kind: int = None) -> Optional[List[Row]]:
|
||||
if item_kind is None:
|
||||
sql = item.select(item.c.user == user_id)
|
||||
else:
|
||||
sql = item.select(and_(item.c.user == user_id, item.c.item_kind == item_kind))
|
||||
sql = item.select(
|
||||
and_(item.c.user == user_id, item.c.item_kind == item_kind)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def get_item(self, user_id: int, item_kind: int, item_id: int) -> Optional[Row]:
|
||||
sql = item.select(and_(item.c.user == user_id, item.c.item_kind == item_kind, item.c.item_id == item_id))
|
||||
sql = item.select(
|
||||
and_(
|
||||
item.c.user == user_id,
|
||||
item.c.item_kind == item_kind,
|
||||
item.c.item_id == item_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_login_bonus(self, user_id: int, bonus_id: int, point: int, is_current: bool, is_complete: bool) -> None:
|
||||
|
||||
def put_login_bonus(
|
||||
self,
|
||||
user_id: int,
|
||||
bonus_id: int,
|
||||
point: int,
|
||||
is_current: bool,
|
||||
is_complete: bool,
|
||||
) -> None:
|
||||
sql = insert(login_bonus).values(
|
||||
user=user_id,
|
||||
bonus_id=bonus_id,
|
||||
@@ -170,25 +224,39 @@ class Mai2ItemData(BaseData):
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_login_bonus: failed to insert item! user_id: {user_id}, bonus_id: {bonus_id}, point: {point}")
|
||||
self.logger.warn(
|
||||
f"put_login_bonus: failed to insert item! user_id: {user_id}, bonus_id: {bonus_id}, point: {point}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_login_bonuses(self, user_id: int) -> Optional[List[Row]]:
|
||||
sql = login_bonus.select(login_bonus.c.user == user_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def get_login_bonus(self, user_id: int, bonus_id: int) -> Optional[Row]:
|
||||
sql = login_bonus.select(and_(login_bonus.c.user == user_id, login_bonus.c.bonus_id == bonus_id))
|
||||
sql = login_bonus.select(
|
||||
and_(login_bonus.c.user == user_id, login_bonus.c.bonus_id == bonus_id)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_map(self, user_id: int, map_id: int, distance: int, is_lock: bool, is_clear: bool, is_complete: bool) -> None:
|
||||
def put_map(
|
||||
self,
|
||||
user_id: int,
|
||||
map_id: int,
|
||||
distance: int,
|
||||
is_lock: bool,
|
||||
is_clear: bool,
|
||||
is_complete: bool,
|
||||
) -> None:
|
||||
sql = insert(map).values(
|
||||
user=user_id,
|
||||
map_id=map_id,
|
||||
@@ -207,25 +275,36 @@ class Mai2ItemData(BaseData):
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_map: failed to insert item! user_id: {user_id}, map_id: {map_id}, distance: {distance}")
|
||||
self.logger.warn(
|
||||
f"put_map: failed to insert item! user_id: {user_id}, map_id: {map_id}, distance: {distance}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_maps(self, user_id: int) -> Optional[List[Row]]:
|
||||
sql = map.select(map.c.user == user_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
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))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_character(self, user_id: int, character_id: int, level: int, awakening: int, use_count: int) -> None:
|
||||
def put_character(
|
||||
self,
|
||||
user_id: int,
|
||||
character_id: int,
|
||||
level: int,
|
||||
awakening: int,
|
||||
use_count: int,
|
||||
) -> None:
|
||||
sql = insert(character).values(
|
||||
user=user_id,
|
||||
character_id=character_id,
|
||||
@@ -242,57 +321,64 @@ class Mai2ItemData(BaseData):
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_character: failed to insert item! user_id: {user_id}, character_id: {character_id}, level: {level}")
|
||||
self.logger.warn(
|
||||
f"put_character: failed to insert item! user_id: {user_id}, character_id: {character_id}, level: {level}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_characters(self, user_id: int) -> Optional[List[Row]]:
|
||||
sql = character.select(character.c.user == user_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def get_character(self, user_id: int, character_id: int) -> Optional[Row]:
|
||||
sql = character.select(and_(character.c.user == user_id, character.c.character_id == character_id))
|
||||
sql = character.select(
|
||||
and_(character.c.user == user_id, character.c.character_id == character_id)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
|
||||
def get_friend_season_ranking(self, user_id: int) -> Optional[Row]:
|
||||
sql = friend_season_ranking.select(friend_season_ranking.c.user == user_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_favorite(self, user_id: int, kind: int, item_id_list: List[int]) -> Optional[int]:
|
||||
|
||||
def put_favorite(
|
||||
self, user_id: int, kind: int, item_id_list: List[int]
|
||||
) -> Optional[int]:
|
||||
sql = insert(favorite).values(
|
||||
user=user_id,
|
||||
kind=kind,
|
||||
item_id_list=item_id_list
|
||||
user=user_id, kind=kind, item_id_list=item_id_list
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
item_id_list=item_id_list
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(item_id_list=item_id_list)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_favorite: failed to insert item! user_id: {user_id}, kind: {kind}")
|
||||
self.logger.warn(
|
||||
f"put_favorite: failed to insert item! user_id: {user_id}, kind: {kind}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_favorites(self, user_id: int, kind: int = None) -> Optional[Row]:
|
||||
if kind is None:
|
||||
sql = favorite.select(favorite.c.user == user_id)
|
||||
else:
|
||||
sql = favorite.select(and_(
|
||||
favorite.c.user == user_id,
|
||||
favorite.c.itemKind == kind
|
||||
))
|
||||
sql = favorite.select(
|
||||
and_(favorite.c.user == user_id, favorite.c.itemKind == kind)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:return None
|
||||
return result.fetchall()
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
@@ -14,7 +14,11 @@ detail = Table(
|
||||
"mai2_profile_detail",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("userName", String(25)),
|
||||
Column("isNetMember", Integer),
|
||||
@@ -41,9 +45,9 @@ detail = Table(
|
||||
Column("lastRomVersion", String(25)),
|
||||
Column("lastDataVersion", String(25)),
|
||||
Column("lastLoginDate", String(25)),
|
||||
Column("lastPairLoginDate", String(25)), # new with uni+
|
||||
Column("lastPairLoginDate", String(25)), # new with uni+
|
||||
Column("lastPlayDate", String(25)),
|
||||
Column("lastTrialPlayDate", String(25)), # new with uni+
|
||||
Column("lastTrialPlayDate", String(25)), # new with uni+
|
||||
Column("lastPlayCredit", Integer),
|
||||
Column("lastPlayMode", Integer),
|
||||
Column("lastPlaceId", Integer),
|
||||
@@ -90,16 +94,20 @@ detail = Table(
|
||||
Column("playerOldRating", BigInteger),
|
||||
Column("playerNewRating", BigInteger),
|
||||
Column("dateTime", BigInteger),
|
||||
Column("banState", Integer), # new with uni+
|
||||
Column("banState", Integer), # new with uni+
|
||||
UniqueConstraint("user", "version", name="mai2_profile_detail_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
ghost = Table(
|
||||
"mai2_profile_ghost",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version_int", Integer, nullable=False),
|
||||
Column("name", String(25)),
|
||||
Column("iconId", Integer),
|
||||
@@ -120,15 +128,21 @@ ghost = Table(
|
||||
Column("resultBitList", JSON),
|
||||
Column("resultNum", Integer),
|
||||
Column("achievement", Integer),
|
||||
UniqueConstraint("user", "version", "musicId", "difficulty", name="mai2_profile_ghost_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
UniqueConstraint(
|
||||
"user", "version", "musicId", "difficulty", name="mai2_profile_ghost_uk"
|
||||
),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
extend = Table(
|
||||
"mai2_profile_extend",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("selectMusicId", Integer),
|
||||
Column("selectDifficultyId", Integer),
|
||||
@@ -145,14 +159,18 @@ extend = Table(
|
||||
Column("selectedCardList", JSON),
|
||||
Column("encountMapNpcList", JSON),
|
||||
UniqueConstraint("user", "version", name="mai2_profile_extend_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
option = Table(
|
||||
"mai2_profile_option",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("selectMusicId", Integer),
|
||||
Column("optionKind", Integer),
|
||||
@@ -200,14 +218,18 @@ option = Table(
|
||||
Column("sortTab", Integer),
|
||||
Column("sortMusic", Integer),
|
||||
UniqueConstraint("user", "version", name="mai2_profile_option_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
rating = Table(
|
||||
"mai2_profile_rating",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("rating", Integer),
|
||||
Column("ratingList", JSON),
|
||||
@@ -216,26 +238,34 @@ rating = Table(
|
||||
Column("nextNewRatingList", JSON),
|
||||
Column("udemae", JSON),
|
||||
UniqueConstraint("user", "version", name="mai2_profile_rating_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
region = Table(
|
||||
"mai2_profile_region",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("regionId", Integer),
|
||||
Column("playCount", Integer, server_default="1"),
|
||||
Column("created", String(25)),
|
||||
UniqueConstraint("user", "regionId", name="mai2_profile_region_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
activity = Table(
|
||||
"mai2_profile_activity",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("kind", Integer, nullable=False),
|
||||
Column("activityId", Integer, nullable=False),
|
||||
Column("param1", Integer, nullable=False),
|
||||
@@ -244,11 +274,14 @@ activity = Table(
|
||||
Column("param4", Integer, nullable=False),
|
||||
Column("sortNumber", Integer, nullable=False),
|
||||
UniqueConstraint("user", "kind", "activityId", name="mai2_profile_activity_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class Mai2ProfileData(BaseData):
|
||||
def put_profile_detail(self, user_id: int, version: int, detail_data: Dict) -> Optional[Row]:
|
||||
def put_profile_detail(
|
||||
self, user_id: int, version: int, detail_data: Dict
|
||||
) -> Optional[Row]:
|
||||
detail_data["user"] = user_id
|
||||
detail_data["version"] = version
|
||||
sql = insert(detail).values(**detail_data)
|
||||
@@ -257,18 +290,25 @@ class Mai2ProfileData(BaseData):
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile: Failed to create profile! user_id {user_id}")
|
||||
self.logger.warn(
|
||||
f"put_profile: Failed to create profile! user_id {user_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_profile_detail(self, user_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(detail).where(and_(detail.c.user == user_id, detail.c.version == version))
|
||||
sql = select(detail).where(
|
||||
and_(detail.c.user == user_id, detail.c.version == version)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_profile_ghost(self, user_id: int, version: int, ghost_data: Dict) -> Optional[int]:
|
||||
def put_profile_ghost(
|
||||
self, user_id: int, version: int, ghost_data: Dict
|
||||
) -> Optional[int]:
|
||||
ghost_data["user"] = user_id
|
||||
ghost_data["version_int"] = version
|
||||
|
||||
@@ -282,13 +322,18 @@ class Mai2ProfileData(BaseData):
|
||||
return result.lastrowid
|
||||
|
||||
def get_profile_ghost(self, user_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(ghost).where(and_(ghost.c.user == user_id, ghost.c.version_int == version))
|
||||
sql = select(ghost).where(
|
||||
and_(ghost.c.user == user_id, ghost.c.version_int == version)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_profile_extend(self, user_id: int, version: int, extend_data: Dict) -> Optional[int]:
|
||||
def put_profile_extend(
|
||||
self, user_id: int, version: int, extend_data: Dict
|
||||
) -> Optional[int]:
|
||||
extend_data["user"] = user_id
|
||||
extend_data["version"] = version
|
||||
|
||||
@@ -302,13 +347,18 @@ class Mai2ProfileData(BaseData):
|
||||
return result.lastrowid
|
||||
|
||||
def get_profile_extend(self, user_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(extend).where(and_(extend.c.user == user_id, extend.c.version == version))
|
||||
sql = select(extend).where(
|
||||
and_(extend.c.user == user_id, extend.c.version == version)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_profile_option(self, user_id: int, version: int, option_data: Dict) -> Optional[int]:
|
||||
def put_profile_option(
|
||||
self, user_id: int, version: int, option_data: Dict
|
||||
) -> Optional[int]:
|
||||
option_data["user"] = user_id
|
||||
option_data["version"] = version
|
||||
|
||||
@@ -322,13 +372,18 @@ class Mai2ProfileData(BaseData):
|
||||
return result.lastrowid
|
||||
|
||||
def get_profile_option(self, user_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(option).where(and_(option.c.user == user_id, option.c.version == version))
|
||||
sql = select(option).where(
|
||||
and_(option.c.user == user_id, option.c.version == version)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_profile_rating(self, user_id: int, version: int, rating_data: Dict) -> Optional[int]:
|
||||
|
||||
def put_profile_rating(
|
||||
self, user_id: int, version: int, rating_data: Dict
|
||||
) -> Optional[int]:
|
||||
rating_data["user"] = user_id
|
||||
rating_data["version"] = version
|
||||
|
||||
@@ -342,23 +397,24 @@ class Mai2ProfileData(BaseData):
|
||||
return result.lastrowid
|
||||
|
||||
def get_profile_rating(self, user_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(rating).where(and_(rating.c.user == user_id, rating.c.version == version))
|
||||
sql = select(rating).where(
|
||||
and_(rating.c.user == user_id, rating.c.version == version)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_profile_region(self, user_id: int, region_id: int) -> Optional[int]:
|
||||
sql = insert(region).values(
|
||||
user = user_id,
|
||||
regionId = region_id,
|
||||
created = datetime.strftime(datetime.now(), Mai2Constants.DATE_TIME_FORMAT)
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
playCount = region.c.playCount + 1
|
||||
user=user_id,
|
||||
regionId=region_id,
|
||||
created=datetime.strftime(datetime.now(), Mai2Constants.DATE_TIME_FORMAT),
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(playCount=region.c.playCount + 1)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_region: failed to update! {user_id}")
|
||||
@@ -369,34 +425,38 @@ class Mai2ProfileData(BaseData):
|
||||
sql = select(region).where(region.c.user == user_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def put_profile_activity(self, user_id: int, activity_data: Dict) -> Optional[int]:
|
||||
if "id" in activity_data:
|
||||
activity_data["activityId"] = activity_data["id"]
|
||||
activity_data.pop("id")
|
||||
|
||||
|
||||
activity_data["user"] = user_id
|
||||
|
||||
|
||||
sql = insert(activity).values(**activity_data)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(**activity_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_activity: failed to update! user_id: {user_id}")
|
||||
self.logger.warn(
|
||||
f"put_profile_activity: failed to update! user_id: {user_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_profile_activity(self, user_id: int, kind: int = None) -> Optional[Row]:
|
||||
sql = activity.select(
|
||||
and_(
|
||||
activity.c.user == user_id,
|
||||
activity.c.user == user_id,
|
||||
(activity.c.kind == kind) if kind is not None else True,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
@@ -12,7 +12,11 @@ best_score = Table(
|
||||
"mai2_score_best",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("musicId", Integer),
|
||||
Column("level", Integer),
|
||||
Column("playCount", Integer),
|
||||
@@ -22,14 +26,18 @@ best_score = Table(
|
||||
Column("deluxscoreMax", Integer),
|
||||
Column("scoreRank", Integer),
|
||||
UniqueConstraint("user", "musicId", "level", name="mai2_score_best_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
playlog = Table(
|
||||
"mai2_playlog",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("userId", BigInteger),
|
||||
Column("orderId", Integer),
|
||||
Column("playlogId", BigInteger),
|
||||
@@ -135,14 +143,19 @@ playlog = Table(
|
||||
Column("isNewFree", Boolean),
|
||||
Column("extNum1", Integer),
|
||||
Column("extNum2", Integer),
|
||||
mysql_charset='utf8mb4'
|
||||
Column("trialPlayAchievement", Integer),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
course = Table(
|
||||
"mai2_score_course",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("courseId", Integer),
|
||||
Column("isLastClear", Boolean),
|
||||
Column("totalRestlife", Integer),
|
||||
@@ -156,47 +169,56 @@ course = Table(
|
||||
Column("bestDeluxscore", Integer),
|
||||
Column("bestDeluxscoreDate", String(25)),
|
||||
UniqueConstraint("user", "courseId", name="mai2_score_best_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class Mai2ScoreData(BaseData):
|
||||
def put_best_score(self, user_id: int, score_data: Dict) -> Optional[int]:
|
||||
score_data["user"] = user_id
|
||||
sql = insert(best_score).values(**score_data)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(**score_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.error(f"put_best_score: Failed to insert best score! user_id {user_id}")
|
||||
self.logger.error(
|
||||
f"put_best_score: Failed to insert best score! user_id {user_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_best_scores(self, user_id: int, song_id: int = None) -> Optional[List[Row]]:
|
||||
sql = best_score.select(
|
||||
and_(
|
||||
best_score.c.user == user_id,
|
||||
(best_score.c.song_id == song_id) if song_id is not None else True
|
||||
)
|
||||
best_score.c.user == user_id,
|
||||
(best_score.c.song_id == song_id) if song_id is not None else True,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_best_score(self, user_id: int, song_id: int, chart_id: int) -> Optional[Row]:
|
||||
|
||||
def get_best_score(
|
||||
self, user_id: int, song_id: int, chart_id: int
|
||||
) -> Optional[Row]:
|
||||
sql = best_score.select(
|
||||
and_(
|
||||
best_score.c.user == user_id,
|
||||
best_score.c.song_id == song_id,
|
||||
best_score.c.chart_id == chart_id
|
||||
)
|
||||
best_score.c.user == user_id,
|
||||
best_score.c.song_id == song_id,
|
||||
best_score.c.chart_id == chart_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def put_playlog(self, user_id: int, playlog_data: Dict) -> Optional[int]:
|
||||
playlog_data["user"] = user_id
|
||||
sql = insert(playlog).values(**playlog_data)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(**playlog_data)
|
||||
@@ -206,8 +228,9 @@ class Mai2ScoreData(BaseData):
|
||||
self.logger.error(f"put_playlog: Failed to insert! user_id {user_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def put_course(self, user_id: int, course_data: Dict) -> Optional[int]:
|
||||
course_data["user"] = user_id
|
||||
sql = insert(course).values(**course_data)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(**course_data)
|
||||
@@ -217,10 +240,11 @@ class Mai2ScoreData(BaseData):
|
||||
self.logger.error(f"put_course: Failed to insert! user_id {user_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_courses(self, user_id: int) -> Optional[List[Row]]:
|
||||
sql = course.select(best_score.c.user == user_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
@@ -12,20 +12,20 @@ event = Table(
|
||||
"mai2_static_event",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("version", Integer,nullable=False),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("eventId", Integer),
|
||||
Column("type", Integer),
|
||||
Column("name", String(255)),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
UniqueConstraint("version", "eventId", "type", name="mai2_static_event_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
music = Table(
|
||||
"mai2_static_music",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("version", Integer,nullable=False),
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("songId", Integer),
|
||||
Column("chartId", Integer),
|
||||
Column("title", String(255)),
|
||||
@@ -36,39 +36,42 @@ music = Table(
|
||||
Column("difficulty", Float),
|
||||
Column("noteDesigner", String(255)),
|
||||
UniqueConstraint("songId", "chartId", "version", name="mai2_static_music_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
ticket = Table(
|
||||
"mai2_static_ticket",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("version", Integer,nullable=False),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("ticketId", Integer),
|
||||
Column("kind", Integer),
|
||||
Column("name", String(255)),
|
||||
Column("price", Integer, server_default="1"),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
UniqueConstraint("version","ticketId", name="mai2_static_ticket_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
UniqueConstraint("version", "ticketId", name="mai2_static_ticket_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class Mai2StaticData(BaseData):
|
||||
def put_game_event(self, version: int, type: int, event_id: int, name: str) -> Optional[int]:
|
||||
def put_game_event(
|
||||
self, version: int, type: int, event_id: int, name: str
|
||||
) -> Optional[int]:
|
||||
sql = insert(event).values(
|
||||
version = version,
|
||||
type = type,
|
||||
eventId = event_id,
|
||||
name = name,
|
||||
version=version,
|
||||
type=type,
|
||||
eventId=event_id,
|
||||
name=name,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
eventId = event_id
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(eventId=event_id)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warning(f"put_game_event: Failed to insert event! event_id {event_id} type {type} name {name}")
|
||||
self.logger.warning(
|
||||
f"put_game_event: Failed to insert event! event_id {event_id} type {type} name {name}"
|
||||
)
|
||||
return result.lastrowid
|
||||
|
||||
def get_game_events(self, version: int) -> Optional[List[Row]]:
|
||||
@@ -78,50 +81,65 @@ class Mai2StaticData(BaseData):
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def get_enabled_events(self, version: int) -> Optional[List[Row]]:
|
||||
sql = select(event).where(and_(
|
||||
event.c.version == version,
|
||||
event.c.enabled == True
|
||||
))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
return result.fetchall()
|
||||
|
||||
def toggle_game_events(self, version: int, event_id: int, toggle: bool) -> Optional[List]:
|
||||
sql = event.update(and_(event.c.version == version, event.c.event_id == event_id)).values(
|
||||
enabled = int(toggle)
|
||||
sql = select(event).where(
|
||||
and_(event.c.version == version, event.c.enabled == True)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
self.logger.warning(f"toggle_game_events: Failed to update event! event_id {event_id} toggle {toggle}")
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def toggle_game_events(
|
||||
self, version: int, event_id: int, toggle: bool
|
||||
) -> Optional[List]:
|
||||
sql = event.update(
|
||||
and_(event.c.version == version, event.c.event_id == event_id)
|
||||
).values(enabled=int(toggle))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"toggle_game_events: Failed to update event! event_id {event_id} toggle {toggle}"
|
||||
)
|
||||
return result.last_updated_params()
|
||||
|
||||
def put_game_music(self, version: int, song_id: int, chart_id: int, title: str, artist: str,
|
||||
genre: str, bpm: str, added_version: str, difficulty: float, note_designer: str) -> None:
|
||||
|
||||
def put_game_music(
|
||||
self,
|
||||
version: int,
|
||||
song_id: int,
|
||||
chart_id: int,
|
||||
title: str,
|
||||
artist: str,
|
||||
genre: str,
|
||||
bpm: str,
|
||||
added_version: str,
|
||||
difficulty: float,
|
||||
note_designer: str,
|
||||
) -> None:
|
||||
sql = insert(music).values(
|
||||
version = version,
|
||||
songId = song_id,
|
||||
chartId = chart_id,
|
||||
title = title,
|
||||
artist = artist,
|
||||
genre = genre,
|
||||
bpm = bpm,
|
||||
addedVersion = added_version,
|
||||
difficulty = difficulty,
|
||||
noteDesigner = note_designer,
|
||||
version=version,
|
||||
songId=song_id,
|
||||
chartId=chart_id,
|
||||
title=title,
|
||||
artist=artist,
|
||||
genre=genre,
|
||||
bpm=bpm,
|
||||
addedVersion=added_version,
|
||||
difficulty=difficulty,
|
||||
noteDesigner=note_designer,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
title = title,
|
||||
artist = artist,
|
||||
genre = genre,
|
||||
bpm = bpm,
|
||||
addedVersion = added_version,
|
||||
difficulty = difficulty,
|
||||
noteDesigner = note_designer,
|
||||
title=title,
|
||||
artist=artist,
|
||||
genre=genre,
|
||||
bpm=bpm,
|
||||
addedVersion=added_version,
|
||||
difficulty=difficulty,
|
||||
noteDesigner=note_designer,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
@@ -129,50 +147,64 @@ class Mai2StaticData(BaseData):
|
||||
self.logger.warn(f"Failed to insert song {song_id} chart {chart_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def put_game_ticket(self, version: int, ticket_id: int, ticket_type: int, ticket_price: int, name: str) -> Optional[int]:
|
||||
|
||||
def put_game_ticket(
|
||||
self,
|
||||
version: int,
|
||||
ticket_id: int,
|
||||
ticket_type: int,
|
||||
ticket_price: int,
|
||||
name: str,
|
||||
) -> Optional[int]:
|
||||
sql = insert(ticket).values(
|
||||
version = version,
|
||||
ticketId = ticket_id,
|
||||
kind = ticket_type,
|
||||
price = ticket_price,
|
||||
name = name
|
||||
version=version,
|
||||
ticketId=ticket_id,
|
||||
kind=ticket_type,
|
||||
price=ticket_price,
|
||||
name=name,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
price = ticket_price
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(price=ticket_price)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"Failed to insert charge {ticket_id} type {ticket_type}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_enabled_tickets(self, version: int, kind: int = None) -> Optional[List[Row]]:
|
||||
|
||||
def get_enabled_tickets(
|
||||
self, version: int, kind: int = None
|
||||
) -> Optional[List[Row]]:
|
||||
if kind is not None:
|
||||
sql = select(ticket).where(and_(
|
||||
ticket.c.version == version,
|
||||
ticket.c.enabled == True,
|
||||
ticket.c.kind == kind
|
||||
))
|
||||
sql = select(ticket).where(
|
||||
and_(
|
||||
ticket.c.version == version,
|
||||
ticket.c.enabled == True,
|
||||
ticket.c.kind == kind,
|
||||
)
|
||||
)
|
||||
else:
|
||||
sql = select(ticket).where(and_(
|
||||
ticket.c.version == version,
|
||||
ticket.c.enabled == True
|
||||
))
|
||||
sql = select(ticket).where(
|
||||
and_(ticket.c.version == version, ticket.c.enabled == True)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_music_chart(self, version: int, song_id: int, chart_id: int) -> Optional[List[Row]]:
|
||||
sql = select(music).where(and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
music.c.chartId == chart_id
|
||||
))
|
||||
def get_music_chart(
|
||||
self, version: int, song_id: int, chart_id: int
|
||||
) -> Optional[List[Row]]:
|
||||
sql = select(music).where(
|
||||
and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
music.c.chartId == chart_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
@@ -8,7 +8,8 @@ from titles.mai2.base import Mai2Base
|
||||
from titles.mai2.config import Mai2Config
|
||||
from titles.mai2.const import Mai2Constants
|
||||
|
||||
|
||||
class Mai2Splash(Mai2Base):
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_SPLASH
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_SPLASH
|
||||
|
||||
@@ -8,7 +8,8 @@ from titles.mai2.base import Mai2Base
|
||||
from titles.mai2.config import Mai2Config
|
||||
from titles.mai2.const import Mai2Constants
|
||||
|
||||
|
||||
class Mai2SplashPlus(Mai2Base):
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_SPLASH_PLUS
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_SPLASH_PLUS
|
||||
|
||||
@@ -8,7 +8,8 @@ from titles.mai2.base import Mai2Base
|
||||
from titles.mai2.const import Mai2Constants
|
||||
from titles.mai2.config import Mai2Config
|
||||
|
||||
|
||||
class Mai2Universe(Mai2Base):
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE
|
||||
|
||||
@@ -8,7 +8,8 @@ from titles.mai2.base import Mai2Base
|
||||
from titles.mai2.const import Mai2Constants
|
||||
from titles.mai2.config import Mai2Config
|
||||
|
||||
|
||||
class Mai2UniversePlus(Mai2Base):
|
||||
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
|
||||
|
||||
@@ -6,13 +6,5 @@ from titles.ongeki.read import OngekiReader
|
||||
index = OngekiServlet
|
||||
database = OngekiData
|
||||
reader = OngekiReader
|
||||
|
||||
use_default_title = True
|
||||
include_protocol = True
|
||||
title_secure = False
|
||||
game_codes = [OngekiConstants.GAME_CODE]
|
||||
trailing_slash = True
|
||||
use_default_host = False
|
||||
host = ""
|
||||
|
||||
current_schema_version = 2
|
||||
current_schema_version = 2
|
||||
|
||||
@@ -11,6 +11,7 @@ from titles.ongeki.config import OngekiConfig
|
||||
from titles.ongeki.database import OngekiData
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
|
||||
|
||||
class OngekiBattleGrade(Enum):
|
||||
FAILED = 0
|
||||
DRAW = 1
|
||||
@@ -21,6 +22,7 @@ class OngekiBattleGrade(Enum):
|
||||
UNBELIEVABLE_GOLD = 6
|
||||
UNBELIEVABLE_RAINBOW = 7
|
||||
|
||||
|
||||
class OngekiBattlePointGrade(Enum):
|
||||
FRESHMAN = 0
|
||||
KYU10 = 1
|
||||
@@ -45,20 +47,22 @@ class OngekiBattlePointGrade(Enum):
|
||||
DAN10 = 20
|
||||
SODEN = 21
|
||||
|
||||
|
||||
class OngekiTechnicalGrade(Enum):
|
||||
D = 0
|
||||
C = 1
|
||||
B = 2
|
||||
BB = 3
|
||||
BBB = 4
|
||||
A = 5
|
||||
AA = 6
|
||||
AAA = 7
|
||||
S = 8
|
||||
SS = 9
|
||||
SSS = 10
|
||||
D = 0
|
||||
C = 1
|
||||
B = 2
|
||||
BB = 3
|
||||
BBB = 4
|
||||
A = 5
|
||||
AA = 6
|
||||
AAA = 7
|
||||
S = 8
|
||||
SS = 9
|
||||
SSS = 10
|
||||
SSSp = 11
|
||||
|
||||
|
||||
class OngekiDifficulty(Enum):
|
||||
BASIC = 0
|
||||
ADVANCED = 1
|
||||
@@ -66,6 +70,7 @@ class OngekiDifficulty(Enum):
|
||||
MASTER = 3
|
||||
LUNATIC = 10
|
||||
|
||||
|
||||
class OngekiGPLogKind(Enum):
|
||||
NONE = 0
|
||||
BUY1_START = 1
|
||||
@@ -82,22 +87,28 @@ class OngekiGPLogKind(Enum):
|
||||
PAY_MAS_UNLOCK = 13
|
||||
PAY_MONEY = 14
|
||||
|
||||
class OngekiBase():
|
||||
|
||||
class OngekiBase:
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.game_cfg = game_cfg
|
||||
self.data = OngekiData(core_cfg)
|
||||
self.date_time_format = "%Y-%m-%d %H:%M:%S"
|
||||
self.date_time_format_ext = "%Y-%m-%d %H:%M:%S.%f" # needs to be lopped off at [:-5]
|
||||
self.date_time_format_ext = (
|
||||
"%Y-%m-%d %H:%M:%S.%f" # needs to be lopped off at [:-5]
|
||||
)
|
||||
self.date_time_format_short = "%Y-%m-%d"
|
||||
self.logger = logging.getLogger("ongeki")
|
||||
self.game = OngekiConstants.GAME_CODE
|
||||
self.version = OngekiConstants.VER_ONGEKI
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
reboot_start = date.strftime(datetime.now() + timedelta(hours=3), self.date_time_format)
|
||||
reboot_end = date.strftime(datetime.now() + timedelta(hours=4), self.date_time_format)
|
||||
reboot_start = date.strftime(
|
||||
datetime.now() + timedelta(hours=3), self.date_time_format
|
||||
)
|
||||
reboot_end = date.strftime(
|
||||
datetime.now() + timedelta(hours=4), self.date_time_format
|
||||
)
|
||||
return {
|
||||
"gameSetting": {
|
||||
"dataVersion": "1.00.00",
|
||||
@@ -128,20 +139,53 @@ class OngekiBase():
|
||||
|
||||
def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
|
||||
return {"length": 0, "gameRankingList": []}
|
||||
|
||||
|
||||
def handle_get_game_point_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
Sets the GP ammount for A and B sets for 1 - 3 crdits
|
||||
"""
|
||||
return {"length":6,"gamePointList":[
|
||||
{"type":0,"cost":100,"startDate":"2000-01-01 05:00:00.0","endDate":"2099-01-01 05:00:00.0"},
|
||||
{"type":1,"cost":200,"startDate":"2000-01-01 05:00:00.0","endDate":"2099-01-01 05:00:00.0"},
|
||||
{"type":2,"cost":300,"startDate":"2000-01-01 05:00:00.0","endDate":"2099-01-01 05:00:00.0"},
|
||||
{"type":3,"cost":120,"startDate":"2000-01-01 05:00:00.0","endDate":"2099-01-01 05:00:00.0"},
|
||||
{"type":4,"cost":240,"startDate":"2000-01-01 05:00:00.0","endDate":"2099-01-01 05:00:00.0"},
|
||||
{"type":5,"cost":360,"startDate":"2000-01-01 05:00:00.0","endDate":"2099-01-01 05:00:00.0"}
|
||||
]}
|
||||
|
||||
return {
|
||||
"length": 6,
|
||||
"gamePointList": [
|
||||
{
|
||||
"type": 0,
|
||||
"cost": 100,
|
||||
"startDate": "2000-01-01 05:00:00.0",
|
||||
"endDate": "2099-01-01 05:00:00.0",
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"cost": 200,
|
||||
"startDate": "2000-01-01 05:00:00.0",
|
||||
"endDate": "2099-01-01 05:00:00.0",
|
||||
},
|
||||
{
|
||||
"type": 2,
|
||||
"cost": 300,
|
||||
"startDate": "2000-01-01 05:00:00.0",
|
||||
"endDate": "2099-01-01 05:00:00.0",
|
||||
},
|
||||
{
|
||||
"type": 3,
|
||||
"cost": 120,
|
||||
"startDate": "2000-01-01 05:00:00.0",
|
||||
"endDate": "2099-01-01 05:00:00.0",
|
||||
},
|
||||
{
|
||||
"type": 4,
|
||||
"cost": 240,
|
||||
"startDate": "2000-01-01 05:00:00.0",
|
||||
"endDate": "2099-01-01 05:00:00.0",
|
||||
},
|
||||
{
|
||||
"type": 5,
|
||||
"cost": 360,
|
||||
"startDate": "2000-01-01 05:00:00.0",
|
||||
"endDate": "2099-01-01 05:00:00.0",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
def handle_game_login_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "gameLogin"}
|
||||
|
||||
@@ -184,11 +228,19 @@ class OngekiBase():
|
||||
|
||||
def handle_upsert_user_gplog_api_request(self, data: Dict) -> Dict:
|
||||
user = data["userId"]
|
||||
if user >= 200000000000000: # Account for guest play
|
||||
if user >= 200000000000000: # Account for guest play
|
||||
user = None
|
||||
|
||||
self.data.log.put_gp_log(user, data["usedCredit"], data["placeName"], data["userGplog"]["trxnDate"],
|
||||
data["userGplog"]["placeId"], data["userGplog"]["kind"], data["userGplog"]["pattern"], data["userGplog"]["currentGP"])
|
||||
self.data.log.put_gp_log(
|
||||
user,
|
||||
data["usedCredit"],
|
||||
data["placeName"],
|
||||
data["userGplog"]["trxnDate"],
|
||||
data["userGplog"]["placeId"],
|
||||
data["userGplog"]["kind"],
|
||||
data["userGplog"]["pattern"],
|
||||
data["userGplog"]["currentGP"],
|
||||
)
|
||||
|
||||
return {"returnCode": 1, "apiName": "UpsertUserGplogApi"}
|
||||
|
||||
@@ -197,39 +249,53 @@ class OngekiBase():
|
||||
|
||||
def handle_get_game_event_api_request(self, data: Dict) -> Dict:
|
||||
evts = self.data.static.get_enabled_events(self.version)
|
||||
|
||||
|
||||
evt_list = []
|
||||
for event in evts:
|
||||
evt_list.append({
|
||||
"type": event["type"],
|
||||
"id": event["eventId"],
|
||||
"startDate": "2017-12-05 07:00:00.0",
|
||||
"endDate": "2099-12-31 00:00:00.0"
|
||||
})
|
||||
|
||||
return {"type": data["type"], "length": len(evt_list), "gameEventList": evt_list}
|
||||
evt_list.append(
|
||||
{
|
||||
"type": event["type"],
|
||||
"id": event["eventId"],
|
||||
"startDate": "2017-12-05 07:00:00.0",
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"type": data["type"],
|
||||
"length": len(evt_list),
|
||||
"gameEventList": evt_list,
|
||||
}
|
||||
|
||||
def handle_get_game_id_list_api_request(self, data: Dict) -> Dict:
|
||||
game_idlist: list[str, Any] = [] #1 to 230 & 8000 to 8050
|
||||
|
||||
game_idlist: list[str, Any] = [] # 1 to 230 & 8000 to 8050
|
||||
|
||||
if data["type"] == 1:
|
||||
for i in range(1,231):
|
||||
for i in range(1, 231):
|
||||
game_idlist.append({"type": 1, "id": i})
|
||||
return {"type": data["type"], "length": len(game_idlist), "gameIdlistList": game_idlist}
|
||||
return {
|
||||
"type": data["type"],
|
||||
"length": len(game_idlist),
|
||||
"gameIdlistList": game_idlist,
|
||||
}
|
||||
elif data["type"] == 2:
|
||||
for i in range(8000,8051):
|
||||
for i in range(8000, 8051):
|
||||
game_idlist.append({"type": 2, "id": i})
|
||||
return {"type": data["type"], "length": len(game_idlist), "gameIdlistList": game_idlist}
|
||||
return {
|
||||
"type": data["type"],
|
||||
"length": len(game_idlist),
|
||||
"gameIdlistList": game_idlist,
|
||||
}
|
||||
|
||||
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
|
||||
return {"userId": data["userId"], "length": 0, "userRegionList": []}
|
||||
|
||||
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
profile = self.data.profile.get_profile_preview(data["userId"], self.version)
|
||||
|
||||
if profile is None:
|
||||
|
||||
if profile is None:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userId": data["userId"],
|
||||
"isLogin": False,
|
||||
"lastLoginDate": "0000-00-00 00:00:00",
|
||||
"userName": "",
|
||||
@@ -240,12 +306,12 @@ class OngekiBase():
|
||||
"lastGameId": "",
|
||||
"lastRomVersion": "",
|
||||
"lastDataVersion": "",
|
||||
"lastPlayDate": "",
|
||||
"lastPlayDate": "",
|
||||
"nameplateId": 0,
|
||||
"trophyId": 0,
|
||||
"cardId": 0,
|
||||
"dispPlayerLv": 0,
|
||||
"dispRating": 0,
|
||||
"trophyId": 0,
|
||||
"cardId": 0,
|
||||
"dispPlayerLv": 0,
|
||||
"dispRating": 0,
|
||||
"dispBP": 0,
|
||||
"headphone": 0,
|
||||
"banStatus": 0,
|
||||
@@ -253,7 +319,7 @@ class OngekiBase():
|
||||
}
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userId": data["userId"],
|
||||
"isLogin": False,
|
||||
"lastLoginDate": profile["lastPlayDate"],
|
||||
"userName": profile["userName"],
|
||||
@@ -264,12 +330,12 @@ class OngekiBase():
|
||||
"lastGameId": profile["lastGameId"],
|
||||
"lastRomVersion": profile["lastRomVersion"],
|
||||
"lastDataVersion": profile["lastDataVersion"],
|
||||
"lastPlayDate": profile["lastPlayDate"],
|
||||
"lastPlayDate": profile["lastPlayDate"],
|
||||
"nameplateId": profile["nameplateId"],
|
||||
"trophyId": profile["trophyId"],
|
||||
"cardId": profile["cardId"],
|
||||
"dispPlayerLv": profile["dispPlayerLv"],
|
||||
"dispRating": profile["dispRating"],
|
||||
"trophyId": profile["trophyId"],
|
||||
"cardId": profile["cardId"],
|
||||
"dispPlayerLv": profile["dispPlayerLv"],
|
||||
"dispRating": profile["dispRating"],
|
||||
"dispBP": profile["dispBP"],
|
||||
"headphone": profile["headphone"],
|
||||
"banStatus": profile["banStatus"],
|
||||
@@ -297,7 +363,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_tech_event_api_request(self, data: Dict) -> Dict:
|
||||
user_tech_event_list = self.data.item.get_tech_event(data["userId"])
|
||||
if user_tech_event_list is None: return {}
|
||||
if user_tech_event_list is None:
|
||||
return {}
|
||||
|
||||
tech_evt = []
|
||||
for evt in user_tech_event_list:
|
||||
@@ -313,11 +380,11 @@ class OngekiBase():
|
||||
}
|
||||
|
||||
def handle_get_user_tech_event_ranking_api_request(self, data: Dict) -> Dict:
|
||||
#user_event_ranking_list = self.data.item.get_tech_event_ranking(data["userId"])
|
||||
#if user_event_ranking_list is None: return {}
|
||||
# user_event_ranking_list = self.data.item.get_tech_event_ranking(data["userId"])
|
||||
# if user_event_ranking_list is None: return {}
|
||||
|
||||
evt_ranking = []
|
||||
#for evt in user_event_ranking_list:
|
||||
# for evt in user_event_ranking_list:
|
||||
# tmp = evt._asdict()
|
||||
# tmp.pop("id")
|
||||
# tmp.pop("user")
|
||||
@@ -331,7 +398,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_kop_api_request(self, data: Dict) -> Dict:
|
||||
kop_list = self.data.profile.get_kop(data["userId"])
|
||||
if kop_list is None: return {}
|
||||
if kop_list is None:
|
||||
return {}
|
||||
|
||||
for kop in kop_list:
|
||||
kop.pop("user")
|
||||
@@ -349,10 +417,10 @@ class OngekiBase():
|
||||
next_idx = data["nextIndex"]
|
||||
start_idx = next_idx
|
||||
end_idx = max_ct + start_idx
|
||||
|
||||
|
||||
if len(song_list[start_idx:]) > max_ct:
|
||||
next_idx += max_ct
|
||||
|
||||
|
||||
else:
|
||||
next_idx = -1
|
||||
|
||||
@@ -360,15 +428,20 @@ class OngekiBase():
|
||||
"userId": data["userId"],
|
||||
"length": len(song_list[start_idx:end_idx]),
|
||||
"nextIndex": next_idx,
|
||||
"userMusicList": song_list[start_idx:end_idx]
|
||||
"userMusicList": song_list[start_idx:end_idx],
|
||||
}
|
||||
|
||||
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||
kind = data["nextIndex"] / 10000000000
|
||||
p = self.data.item.get_items(data["userId"], kind)
|
||||
|
||||
if p is None:
|
||||
return {"userId": data["userId"], "nextIndex": -1, "itemKind": kind, "userItemList": []}
|
||||
if p is None:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"nextIndex": -1,
|
||||
"itemKind": kind,
|
||||
"userItemList": [],
|
||||
}
|
||||
|
||||
items: list[Dict[str, Any]] = []
|
||||
for i in range(data["nextIndex"] % 10000000000, len(p)):
|
||||
@@ -381,14 +454,23 @@ class OngekiBase():
|
||||
|
||||
xout = kind * 10000000000 + (data["nextIndex"] % 10000000000) + len(items)
|
||||
|
||||
if len(items) < data["maxCount"] or data["maxCount"] == 0: nextIndex = 0
|
||||
else: nextIndex = xout
|
||||
if len(items) < data["maxCount"] or data["maxCount"] == 0:
|
||||
nextIndex = 0
|
||||
else:
|
||||
nextIndex = xout
|
||||
|
||||
return {"userId": data["userId"], "nextIndex": int(nextIndex), "itemKind": int(kind), "length": len(items), "userItemList": items}
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"nextIndex": int(nextIndex),
|
||||
"itemKind": int(kind),
|
||||
"length": len(items),
|
||||
"userItemList": items,
|
||||
}
|
||||
|
||||
def handle_get_user_option_api_request(self, data: Dict) -> Dict:
|
||||
o = self.data.profile.get_profile_options(data["userId"])
|
||||
if o is None: return {}
|
||||
if o is None:
|
||||
return {}
|
||||
|
||||
# get the dict representation of the row so we can modify values
|
||||
user_opts = o._asdict()
|
||||
@@ -401,14 +483,17 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||
p = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||
if p is None: return {}
|
||||
if p is None:
|
||||
return {}
|
||||
|
||||
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']}")
|
||||
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()
|
||||
|
||||
@@ -422,14 +507,14 @@ class OngekiBase():
|
||||
# add access code that we don't store
|
||||
user_data["accessCode"] = cards[0]["access_code"]
|
||||
|
||||
return {"userId": data["userId"], "userData":user_data}
|
||||
return {"userId": data["userId"], "userData": user_data}
|
||||
|
||||
def handle_get_user_event_ranking_api_request(self, data: Dict) -> Dict:
|
||||
#user_event_ranking_list = self.data.item.get_event_ranking(data["userId"])
|
||||
#if user_event_ranking_list is None: return {}
|
||||
# user_event_ranking_list = self.data.item.get_event_ranking(data["userId"])
|
||||
# if user_event_ranking_list is None: return {}
|
||||
|
||||
evt_ranking = []
|
||||
#for evt in user_event_ranking_list:
|
||||
# for evt in user_event_ranking_list:
|
||||
# tmp = evt._asdict()
|
||||
# tmp.pop("id")
|
||||
# tmp.pop("user")
|
||||
@@ -443,7 +528,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
||||
user_login_bonus_list = self.data.item.get_login_bonuses(data["userId"])
|
||||
if user_login_bonus_list is None: return {}
|
||||
if user_login_bonus_list is None:
|
||||
return {}
|
||||
|
||||
login_bonuses = []
|
||||
for scenerio in user_login_bonus_list:
|
||||
@@ -451,16 +537,19 @@ class OngekiBase():
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
login_bonuses.append(tmp)
|
||||
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(login_bonuses),
|
||||
"userLoginBonusList": login_bonuses
|
||||
"userId": data["userId"],
|
||||
"length": len(login_bonuses),
|
||||
"userLoginBonusList": login_bonuses,
|
||||
}
|
||||
|
||||
def handle_get_user_bp_base_request(self, data: Dict) -> Dict:
|
||||
p = self.data.profile.get_profile(self.game, self.version, user_id = data["userId"])
|
||||
if p is None: return {}
|
||||
p = self.data.profile.get_profile(
|
||||
self.game, self.version, user_id=data["userId"]
|
||||
)
|
||||
if p is None:
|
||||
return {}
|
||||
profile = json.loads(p["data"])
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
@@ -470,7 +559,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
|
||||
recent_rating = self.data.profile.get_profile_recent_rating(data["userId"])
|
||||
if recent_rating is None: return {}
|
||||
if recent_rating is None:
|
||||
return {}
|
||||
|
||||
userRecentRatingList = recent_rating["recentRating"]
|
||||
|
||||
@@ -482,31 +572,35 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
||||
activity = self.data.profile.get_profile_activity(data["userId"], data["kind"])
|
||||
if activity is None: return {}
|
||||
|
||||
if activity is None:
|
||||
return {}
|
||||
|
||||
user_activity = []
|
||||
|
||||
|
||||
for act in activity:
|
||||
user_activity.append({
|
||||
"kind": act["kind"],
|
||||
"id": act["activityId"],
|
||||
"sortNumber": act["sortNumber"],
|
||||
"param1": act["param1"],
|
||||
"param2": act["param2"],
|
||||
"param3": act["param3"],
|
||||
"param4": act["param4"],
|
||||
})
|
||||
user_activity.append(
|
||||
{
|
||||
"kind": act["kind"],
|
||||
"id": act["activityId"],
|
||||
"sortNumber": act["sortNumber"],
|
||||
"param1": act["param1"],
|
||||
"param2": act["param2"],
|
||||
"param3": act["param3"],
|
||||
"param4": act["param4"],
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userId": data["userId"],
|
||||
"length": len(user_activity),
|
||||
"kind": data["kind"],
|
||||
"userActivityList": user_activity
|
||||
"userActivityList": user_activity,
|
||||
}
|
||||
|
||||
def handle_get_user_story_api_request(self, data: Dict) -> Dict:
|
||||
user_stories = self.data.item.get_stories(data["userId"])
|
||||
if user_stories is None: return {}
|
||||
if user_stories is None:
|
||||
return {}
|
||||
|
||||
story_list = []
|
||||
for story in user_stories:
|
||||
@@ -516,14 +610,15 @@ class OngekiBase():
|
||||
story_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(story_list),
|
||||
"userStoryList": story_list
|
||||
"userId": data["userId"],
|
||||
"length": len(story_list),
|
||||
"userStoryList": story_list,
|
||||
}
|
||||
|
||||
def handle_get_user_chapter_api_request(self, data: Dict) -> Dict:
|
||||
user_chapters = self.data.item.get_chapters(data["userId"])
|
||||
if user_chapters is None: return {}
|
||||
if user_chapters is None:
|
||||
return {}
|
||||
|
||||
chapter_list = []
|
||||
for chapter in user_chapters:
|
||||
@@ -531,11 +626,11 @@ class OngekiBase():
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
chapter_list.append(tmp)
|
||||
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(chapter_list),
|
||||
"userChapterList": chapter_list
|
||||
"userId": data["userId"],
|
||||
"length": len(chapter_list),
|
||||
"userChapterList": chapter_list,
|
||||
}
|
||||
|
||||
def handle_get_user_training_room_by_key_api_request(self, data: Dict) -> Dict:
|
||||
@@ -547,7 +642,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
|
||||
user_characters = self.data.item.get_characters(data["userId"])
|
||||
if user_characters is None: return {}
|
||||
if user_characters is None:
|
||||
return {}
|
||||
|
||||
character_list = []
|
||||
for character in user_characters:
|
||||
@@ -555,16 +651,17 @@ class OngekiBase():
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
character_list.append(tmp)
|
||||
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(character_list),
|
||||
"userCharacterList": character_list
|
||||
"userId": data["userId"],
|
||||
"length": len(character_list),
|
||||
"userCharacterList": character_list,
|
||||
}
|
||||
|
||||
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 {}
|
||||
if user_cards is None:
|
||||
return {}
|
||||
|
||||
card_list = []
|
||||
for card in user_cards:
|
||||
@@ -572,17 +669,18 @@ class OngekiBase():
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
card_list.append(tmp)
|
||||
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(card_list),
|
||||
"userCardList": card_list
|
||||
"userId": data["userId"],
|
||||
"length": len(card_list),
|
||||
"userCardList": card_list,
|
||||
}
|
||||
|
||||
def handle_get_user_deck_by_key_api_request(self, data: Dict) -> Dict:
|
||||
# Auth key doesn't matter, it just wants all the decks
|
||||
decks = self.data.item.get_decks(data["userId"])
|
||||
if decks is None: return {}
|
||||
if decks is None:
|
||||
return {}
|
||||
|
||||
deck_list = []
|
||||
for deck in decks:
|
||||
@@ -599,7 +697,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_trade_item_api_request(self, data: Dict) -> Dict:
|
||||
user_trade_items = self.data.item.get_trade_items(data["userId"])
|
||||
if user_trade_items is None: return {}
|
||||
if user_trade_items is None:
|
||||
return {}
|
||||
|
||||
trade_item_list = []
|
||||
for trade_item in user_trade_items:
|
||||
@@ -616,7 +715,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_scenario_api_request(self, data: Dict) -> Dict:
|
||||
user_scenerio = self.data.item.get_scenerios(data["userId"])
|
||||
if user_scenerio is None: return {}
|
||||
if user_scenerio is None:
|
||||
return {}
|
||||
|
||||
scenerio_list = []
|
||||
for scenerio in user_scenerio:
|
||||
@@ -624,7 +724,7 @@ class OngekiBase():
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
scenerio_list.append(tmp)
|
||||
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(scenerio_list),
|
||||
@@ -633,7 +733,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_ratinglog_api_request(self, data: Dict) -> Dict:
|
||||
rating_log = self.data.profile.get_profile_rating_log(data["userId"])
|
||||
if rating_log is None: return {}
|
||||
if rating_log is None:
|
||||
return {}
|
||||
|
||||
userRatinglogList = []
|
||||
for rating in rating_log:
|
||||
@@ -650,7 +751,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_mission_point_api_request(self, data: Dict) -> Dict:
|
||||
user_mission_point_list = self.data.item.get_mission_points(data["userId"])
|
||||
if user_mission_point_list is None: return {}
|
||||
if user_mission_point_list is None:
|
||||
return {}
|
||||
|
||||
mission_point_list = []
|
||||
for evt_music in user_mission_point_list:
|
||||
@@ -667,7 +769,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_event_point_api_request(self, data: Dict) -> Dict:
|
||||
user_event_point_list = self.data.item.get_event_points(data["userId"])
|
||||
if user_event_point_list is None: return {}
|
||||
if user_event_point_list is None:
|
||||
return {}
|
||||
|
||||
event_point_list = []
|
||||
for evt_music in user_event_point_list:
|
||||
@@ -684,7 +787,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_music_item_api_request(self, data: Dict) -> Dict:
|
||||
user_music_item_list = self.data.item.get_music_items(data["userId"])
|
||||
if user_music_item_list is None: return {}
|
||||
if user_music_item_list is None:
|
||||
return {}
|
||||
|
||||
music_item_list = []
|
||||
for evt_music in user_music_item_list:
|
||||
@@ -701,7 +805,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_event_music_api_request(self, data: Dict) -> Dict:
|
||||
user_evt_music_list = self.data.item.get_event_music(data["userId"])
|
||||
if user_evt_music_list is None: return {}
|
||||
if user_evt_music_list is None:
|
||||
return {}
|
||||
|
||||
evt_music_list = []
|
||||
for evt_music in user_evt_music_list:
|
||||
@@ -718,7 +823,8 @@ class OngekiBase():
|
||||
|
||||
def handle_get_user_boss_api_request(self, data: Dict) -> Dict:
|
||||
p = self.data.item.get_bosses(data["userId"])
|
||||
if p is None: return {}
|
||||
if p is None:
|
||||
return {}
|
||||
|
||||
boss_list = []
|
||||
for boss in p:
|
||||
@@ -740,7 +846,9 @@ class OngekiBase():
|
||||
# The isNew fields are new as of Red and up. We just won't use them for now.
|
||||
|
||||
if "userData" in upsert and len(upsert["userData"]) > 0:
|
||||
self.data.profile.put_profile_data(user_id, self.version, upsert["userData"][0])
|
||||
self.data.profile.put_profile_data(
|
||||
user_id, self.version, upsert["userData"][0]
|
||||
)
|
||||
|
||||
if "userOption" in upsert and len(upsert["userOption"]) > 0:
|
||||
self.data.profile.put_profile_options(user_id, upsert["userOption"][0])
|
||||
@@ -751,27 +859,37 @@ class OngekiBase():
|
||||
|
||||
if "userActivityList" in upsert:
|
||||
for act in upsert["userActivityList"]:
|
||||
self.data.profile.put_profile_activity(user_id, act["kind"], act["id"], act["sortNumber"], act["param1"],
|
||||
act["param2"], act["param3"], act["param4"])
|
||||
|
||||
self.data.profile.put_profile_activity(
|
||||
user_id,
|
||||
act["kind"],
|
||||
act["id"],
|
||||
act["sortNumber"],
|
||||
act["param1"],
|
||||
act["param2"],
|
||||
act["param3"],
|
||||
act["param4"],
|
||||
)
|
||||
|
||||
if "userRecentRatingList" in upsert:
|
||||
self.data.profile.put_profile_recent_rating(user_id, upsert["userRecentRatingList"])
|
||||
|
||||
self.data.profile.put_profile_recent_rating(
|
||||
user_id, upsert["userRecentRatingList"]
|
||||
)
|
||||
|
||||
if "userBpBaseList" in upsert:
|
||||
self.data.profile.put_profile_bp_list(user_id, upsert["userBpBaseList"])
|
||||
|
||||
|
||||
if "userMusicDetailList" in upsert:
|
||||
for x in upsert["userMusicDetailList"]:
|
||||
self.data.score.put_best_score(user_id, x)
|
||||
|
||||
|
||||
if "userCharacterList" in upsert:
|
||||
for x in upsert["userCharacterList"]:
|
||||
self.data.item.put_character(user_id, x)
|
||||
|
||||
|
||||
if "userCardList" in upsert:
|
||||
for x in upsert["userCardList"]:
|
||||
self.data.item.put_card(user_id, x)
|
||||
|
||||
|
||||
if "userDeckList" in upsert:
|
||||
for x in upsert["userDeckList"]:
|
||||
self.data.item.put_deck(user_id, x)
|
||||
@@ -779,39 +897,45 @@ class OngekiBase():
|
||||
if "userTrainingRoomList" in upsert:
|
||||
for x in upsert["userTrainingRoomList"]:
|
||||
self.data.profile.put_training_room(user_id, x)
|
||||
|
||||
|
||||
if "userStoryList" in upsert:
|
||||
for x in upsert["userStoryList"]:
|
||||
self.data.item.put_story(user_id, x)
|
||||
|
||||
|
||||
if "userChapterList" in upsert:
|
||||
for x in upsert["userChapterList"]:
|
||||
self.data.item.put_chapter(user_id, x)
|
||||
|
||||
|
||||
if "userMemoryChapterList" in upsert:
|
||||
for x in upsert["userMemoryChapterList"]:
|
||||
self.data.item.put_memorychapter(user_id, x)
|
||||
|
||||
if "userItemList" in upsert:
|
||||
for x in upsert["userItemList"]:
|
||||
self.data.item.put_item(user_id, x)
|
||||
|
||||
|
||||
if "userMusicItemList" in upsert:
|
||||
for x in upsert["userMusicItemList"]:
|
||||
self.data.item.put_music_item(user_id, x)
|
||||
|
||||
|
||||
if "userLoginBonusList" in upsert:
|
||||
for x in upsert["userLoginBonusList"]:
|
||||
self.data.item.put_login_bonus(user_id, x)
|
||||
|
||||
|
||||
if "userEventPointList" in upsert:
|
||||
for x in upsert["userEventPointList"]:
|
||||
self.data.item.put_event_point(user_id, x)
|
||||
|
||||
|
||||
if "userMissionPointList" in upsert:
|
||||
for x in upsert["userMissionPointList"]:
|
||||
self.data.item.put_mission_point(user_id, x)
|
||||
|
||||
|
||||
if "userRatinglogList" in upsert:
|
||||
for x in upsert["userRatinglogList"]:
|
||||
self.data.profile.put_profile_rating_log(user_id, x["dataVersion"], x["highestRating"])
|
||||
|
||||
self.data.profile.put_profile_rating_log(
|
||||
user_id, x["dataVersion"], x["highestRating"]
|
||||
)
|
||||
|
||||
if "userBossList" in upsert:
|
||||
for x in upsert["userBossList"]:
|
||||
self.data.item.put_boss(user_id, x)
|
||||
@@ -840,7 +964,7 @@ class OngekiBase():
|
||||
for x in upsert["userKopList"]:
|
||||
self.data.profile.put_kop(user_id, x)
|
||||
|
||||
return {'returnCode': 1, 'apiName': 'upsertUserAll'}
|
||||
return {"returnCode": 1, "apiName": "upsertUserAll"}
|
||||
|
||||
def handle_get_user_rival_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
@@ -853,29 +977,28 @@ class OngekiBase():
|
||||
"length": 0,
|
||||
"userRivalList": [],
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(rival_list),
|
||||
"userRivalList": rival_list._asdict(),
|
||||
}
|
||||
|
||||
def handle_get_user_rival_data_api_reqiest(self, data:Dict) -> Dict:
|
||||
def handle_get_user_rival_data_api_reqiest(self, data: Dict) -> Dict:
|
||||
"""
|
||||
Added in Bright
|
||||
"""
|
||||
rivals = []
|
||||
|
||||
for rival in data["userRivalList"]:
|
||||
name = self.data.profile.get_profile_name(rival["rivalUserId"], self.version)
|
||||
name = self.data.profile.get_profile_name(
|
||||
rival["rivalUserId"], self.version
|
||||
)
|
||||
if name is None:
|
||||
continue
|
||||
|
||||
rivals.append({
|
||||
"rivalUserId": rival["rival"],
|
||||
"rivalUserName": name
|
||||
})
|
||||
|
||||
rivals.append({"rivalUserId": rival["rival"], "rivalUserName": name})
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(rivals),
|
||||
@@ -889,11 +1012,9 @@ class OngekiBase():
|
||||
rival_id = data["rivalUserId"]
|
||||
next_idx = data["nextIndex"]
|
||||
max_ct = data["maxCount"]
|
||||
music = self.handle_get_user_music_api_request({
|
||||
"userId": rival_id,
|
||||
"nextIndex": next_idx,
|
||||
"maxCount": max_ct
|
||||
})
|
||||
music = self.handle_get_user_music_api_request(
|
||||
{"userId": rival_id, "nextIndex": next_idx, "maxCount": max_ct}
|
||||
)
|
||||
|
||||
for song in music["userMusicList"]:
|
||||
song["userRivalMusicDetailList"] = song["userMusicDetailList"]
|
||||
@@ -917,18 +1038,15 @@ class OngekiBase():
|
||||
tmp = md._asdict()
|
||||
tmp.pop("user")
|
||||
tmp.pop("id")
|
||||
|
||||
|
||||
for song in song_list:
|
||||
if song["userMusicDetailList"][0]["musicId"] == tmp["musicId"]:
|
||||
found = True
|
||||
song["userMusicDetailList"].append(tmp)
|
||||
song["length"] = len(song["userMusicDetailList"])
|
||||
break
|
||||
|
||||
|
||||
if not found:
|
||||
song_list.append({
|
||||
"length": 1,
|
||||
"userMusicDetailList": [tmp]
|
||||
})
|
||||
|
||||
song_list.append({"length": 1, "userMusicDetailList": [tmp]})
|
||||
|
||||
return song_list
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from datetime import date, datetime, timedelta
|
||||
from typing import Any, Dict
|
||||
from random import randint
|
||||
import pytz
|
||||
import json
|
||||
|
||||
@@ -8,8 +9,8 @@ from titles.ongeki.base import OngekiBase
|
||||
from titles.ongeki.const import OngekiConstants
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
|
||||
class OngekiBright(OngekiBase):
|
||||
|
||||
class OngekiBright(OngekiBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_BRIGHT
|
||||
@@ -19,3 +20,602 @@ class OngekiBright(OngekiBase):
|
||||
ret["gameSetting"]["dataVersion"] = "1.30.00"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.30.00"
|
||||
return ret
|
||||
|
||||
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||
# check for a bright profile
|
||||
p = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||
if p is None:
|
||||
return {}
|
||||
|
||||
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")
|
||||
|
||||
# TODO: replace datetime objects with strings
|
||||
|
||||
# add access code that we don't store
|
||||
user_data["accessCode"] = cards[0]["access_code"]
|
||||
|
||||
# hardcode Card Maker version for now
|
||||
# Card Maker 1.34.00 = 1.30.01
|
||||
# Card Maker 1.36.00 = 1.35.04
|
||||
user_data["compatibleCmVersion"] = "1.30.01"
|
||||
|
||||
return {"userId": data["userId"], "userData": user_data}
|
||||
|
||||
def handle_printer_login_api_request(self, data: Dict):
|
||||
return {"returnCode": 1}
|
||||
|
||||
def handle_printer_logout_api_request(self, data: Dict):
|
||||
return {"returnCode": 1}
|
||||
|
||||
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 {}
|
||||
|
||||
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 = -1
|
||||
|
||||
card_list = []
|
||||
for card in user_cards:
|
||||
tmp = card._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
card_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(card_list[start_idx:end_idx]),
|
||||
"nextIndex": next_idx,
|
||||
"userCardList": card_list[start_idx:end_idx],
|
||||
}
|
||||
|
||||
def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
|
||||
user_characters = self.data.item.get_characters(data["userId"])
|
||||
if user_characters is None:
|
||||
return {}
|
||||
|
||||
max_ct = data["maxCount"]
|
||||
next_idx = data["nextIndex"]
|
||||
start_idx = next_idx
|
||||
end_idx = max_ct + start_idx
|
||||
|
||||
if len(user_characters[start_idx:]) > max_ct:
|
||||
next_idx += max_ct
|
||||
else:
|
||||
next_idx = -1
|
||||
|
||||
character_list = []
|
||||
for character in user_characters:
|
||||
tmp = character._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
character_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(character_list[start_idx:end_idx]),
|
||||
"nextIndex": next_idx,
|
||||
"userCharacterList": character_list[start_idx:end_idx],
|
||||
}
|
||||
|
||||
def handle_get_user_gacha_api_request(self, data: Dict) -> Dict:
|
||||
user_gachas = self.data.item.get_user_gachas(data["userId"])
|
||||
if user_gachas is None:
|
||||
return {"userId": data["userId"], "length": 0, "userGachaList": []}
|
||||
|
||||
user_gacha_list = []
|
||||
for gacha in user_gachas:
|
||||
tmp = gacha._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
tmp["dailyGachaDate"] = datetime.strftime(tmp["dailyGachaDate"], "%Y-%m-%d")
|
||||
user_gacha_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(user_gacha_list),
|
||||
"userGachaList": user_gacha_list,
|
||||
}
|
||||
|
||||
def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||
return self.handle_get_user_item_api_request(data)
|
||||
|
||||
def handle_cm_get_user_gacha_supply_api_request(self, data: Dict) -> Dict:
|
||||
# not used for now? not sure what it even does
|
||||
user_gacha_supplies = self.data.item.get_user_gacha_supplies(data["userId"])
|
||||
if user_gacha_supplies is None:
|
||||
return {"supplyId": 1, "length": 0, "supplyCardList": []}
|
||||
|
||||
supply_list = [gacha["cardId"] for gacha in user_gacha_supplies]
|
||||
|
||||
return {
|
||||
"supplyId": 1,
|
||||
"length": len(supply_list),
|
||||
"supplyCardList": supply_list,
|
||||
}
|
||||
|
||||
def handle_get_game_gacha_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
returns all current active banners (gachas)
|
||||
"Select Gacha" requires maxSelectPoint set and isCeiling set to 1
|
||||
"""
|
||||
game_gachas = []
|
||||
# for every gacha_id in the OngekiConfig, grab the banner from the db
|
||||
for gacha_id in self.game_cfg.gachas.enabled_gachas:
|
||||
game_gacha = self.data.static.get_gacha(self.version, gacha_id)
|
||||
if game_gacha:
|
||||
game_gachas.append(game_gacha)
|
||||
|
||||
# clean the database rows
|
||||
game_gacha_list = []
|
||||
for gacha in game_gachas:
|
||||
tmp = gacha._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("version")
|
||||
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"
|
||||
)
|
||||
tmp["convertEndDate"] = datetime.strftime(
|
||||
tmp["convertEndDate"], "%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
|
||||
# make sure to only show gachas for the current version
|
||||
# so only up to bright, 1140 is the first bright memory gacha
|
||||
if self.version == OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY:
|
||||
game_gacha_list.append(tmp)
|
||||
elif (
|
||||
self.version == OngekiConstants.VER_ONGEKI_BRIGHT
|
||||
and tmp["gachaId"] < 1140
|
||||
):
|
||||
game_gacha_list.append(tmp)
|
||||
|
||||
return {
|
||||
"length": len(game_gacha_list),
|
||||
"gameGachaList": game_gacha_list,
|
||||
# no clue
|
||||
"registIdList": [],
|
||||
}
|
||||
|
||||
def handle_roll_gacha_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
Handle a gacha roll API request
|
||||
"""
|
||||
gacha_id = data["gachaId"]
|
||||
num_rolls = data["times"]
|
||||
# change_rate is the 5 gacha rool SR gurantee once a week
|
||||
change_rate = data["changeRate"]
|
||||
# SSR book which guarantees a SSR card, itemKind=15, itemId=1
|
||||
book_used = data["bookUseCount"]
|
||||
if num_rolls not in {1, 5, 11}:
|
||||
return {}
|
||||
|
||||
# https://gamerch.com/ongeki/entry/462978
|
||||
|
||||
# 77% chance of gett ing a R card
|
||||
# 20% chance of getting a SR card
|
||||
# 3% chance of getting a SSR card
|
||||
rarity = [1 for _ in range(77)]
|
||||
rarity += [2 for _ in range(20)]
|
||||
rarity += [3 for _ in range(3)]
|
||||
|
||||
# gachaId 1011 is "無料ガチャ" (free gacha), which requires GatchaTickets
|
||||
# itemKind=11, itemId=1 and appearenty sucks
|
||||
# 94% chance of getting a R card
|
||||
# 5% chance of getting a SR card
|
||||
# 1% chance of getting a SSR card
|
||||
if gacha_id == 1011:
|
||||
rarity = [1 for _ in range(94)]
|
||||
rarity += [2 for _ in range(5)]
|
||||
rarity += [3 for _ in range(1)]
|
||||
|
||||
# gachaId 1012 is "無料ガチャ(SR確定)" (SR confirmed! free gacha), which
|
||||
# requires GatchaTickets itemKind=11, itemId=4 and always guarantees
|
||||
# a SR card or higher
|
||||
# 92% chance of getting a SR card
|
||||
# 8% chance of getting a SSR card
|
||||
elif gacha_id == 1012:
|
||||
rarity = [2 for _ in range(92)]
|
||||
rarity += [3 for _ in range(8)]
|
||||
|
||||
assert len(rarity) == 100
|
||||
|
||||
# uniform distribution to get the rarity of the card
|
||||
rolls = [rarity[randint(0, len(rarity) - 1)] for _ in range(num_rolls)]
|
||||
|
||||
# if SSR book used, make sure you always get one SSR
|
||||
if book_used == 1:
|
||||
if rolls.count(3) == 0:
|
||||
# if there is no SSR, re-roll
|
||||
return self.handle_roll_gacha_api_request(data)
|
||||
# make sure that 11 rolls always have at least 1 SR or SSR
|
||||
elif (num_rolls == 5 and change_rate is True) or num_rolls == 11:
|
||||
if rolls.count(2) == 0 and rolls.count(3) == 0:
|
||||
# if there is no SR or SSR, re-roll
|
||||
return self.handle_roll_gacha_api_request(data)
|
||||
|
||||
# get a list of cards for each rarity
|
||||
cards_r = self.data.static.get_cards_by_rarity(self.version, 1)
|
||||
cards_sr, cards_ssr = [], []
|
||||
|
||||
# free gachas are only allowed to get their specific cards! (R irrelevant)
|
||||
if gacha_id in {1011, 1012}:
|
||||
gacha_cards = self.data.static.get_gacha_cards(gacha_id)
|
||||
for card in gacha_cards:
|
||||
if card["rarity"] == 3:
|
||||
cards_sr.append({"cardId": card["cardId"], "rarity": 2})
|
||||
elif card["rarity"] == 4:
|
||||
cards_ssr.append({"cardId": card["cardId"], "rarity": 3})
|
||||
else:
|
||||
cards_sr = self.data.static.get_cards_by_rarity(self.version, 2)
|
||||
cards_ssr = self.data.static.get_cards_by_rarity(self.version, 3)
|
||||
|
||||
# get the promoted cards for that gacha and add them multiple
|
||||
# times to increase chances by factor chances
|
||||
chances = 10
|
||||
|
||||
gacha_cards = self.data.static.get_gacha_cards(gacha_id)
|
||||
for card in gacha_cards:
|
||||
# make sure to add the cards to the corresponding rarity
|
||||
if card["rarity"] == 2:
|
||||
cards_r += [{"cardId": card["cardId"], "rarity": 1}] * chances
|
||||
if card["rarity"] == 3:
|
||||
cards_sr += [{"cardId": card["cardId"], "rarity": 2}] * chances
|
||||
elif card["rarity"] == 4:
|
||||
cards_ssr += [{"cardId": card["cardId"], "rarity": 3}] * chances
|
||||
|
||||
# get the card id for each roll
|
||||
rolled_cards = []
|
||||
for i in range(len(rolls)):
|
||||
if rolls[i] == 1:
|
||||
rolled_cards.append(cards_r[randint(0, len(cards_r) - 1)])
|
||||
elif rolls[i] == 2:
|
||||
rolled_cards.append(cards_sr[randint(0, len(cards_sr) - 1)])
|
||||
elif rolls[i] == 3:
|
||||
rolled_cards.append(cards_ssr[randint(0, len(cards_ssr) - 1)])
|
||||
|
||||
game_gacha_card_list = []
|
||||
for card in rolled_cards:
|
||||
game_gacha_card_list.append(
|
||||
{
|
||||
"gachaId": data["gachaId"],
|
||||
"cardId": card["cardId"],
|
||||
# +1 because Card Maker is weird
|
||||
"rarity": card["rarity"] + 1,
|
||||
"weight": 1,
|
||||
"isPickup": False,
|
||||
"isSelect": False,
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"length": len(game_gacha_card_list),
|
||||
"gameGachaCardList": game_gacha_card_list,
|
||||
}
|
||||
|
||||
def handle_cm_upsert_user_gacha_api_request(self, data: Dict):
|
||||
upsert = data["cmUpsertUserGacha"]
|
||||
user_id = data["userId"]
|
||||
|
||||
gacha_id = data["gachaId"]
|
||||
gacha_count = data["gachaCnt"]
|
||||
play_date = datetime.strptime(data["playDate"][:10], "%Y-%m-%d")
|
||||
select_point = data["selectPoint"]
|
||||
|
||||
total_gacha_count, ceiling_gacha_count = 0, 0
|
||||
daily_gacha_cnt, five_gacha_cnt, eleven_gacha_cnt = 0, 0, 0
|
||||
daily_gacha_date = datetime.strptime("2000-01-01", "%Y-%m-%d")
|
||||
|
||||
# check if the user previously rolled the exact same gacha
|
||||
user_gacha = self.data.item.get_user_gacha(user_id, gacha_id)
|
||||
if user_gacha:
|
||||
total_gacha_count = user_gacha["totalGachaCnt"]
|
||||
ceiling_gacha_count = user_gacha["ceilingGachaCnt"]
|
||||
daily_gacha_cnt = user_gacha["dailyGachaCnt"]
|
||||
five_gacha_cnt = user_gacha["fiveGachaCnt"]
|
||||
eleven_gacha_cnt = user_gacha["elevenGachaCnt"]
|
||||
# parse just the year, month and date
|
||||
daily_gacha_date = user_gacha["dailyGachaDate"]
|
||||
|
||||
# if the saved dailyGachaDate is different from the roll,
|
||||
# reset dailyGachaCnt and change the date
|
||||
if daily_gacha_date != play_date:
|
||||
daily_gacha_date = play_date
|
||||
daily_gacha_cnt = 0
|
||||
|
||||
self.data.item.put_user_gacha(
|
||||
user_id,
|
||||
gacha_id,
|
||||
totalGachaCnt=total_gacha_count + gacha_count,
|
||||
ceilingGachaCnt=ceiling_gacha_count + gacha_count,
|
||||
selectPoint=select_point,
|
||||
useSelectPoint=0,
|
||||
dailyGachaCnt=daily_gacha_cnt + gacha_count,
|
||||
fiveGachaCnt=five_gacha_cnt + 1 if gacha_count == 5 else five_gacha_cnt,
|
||||
elevenGachaCnt=eleven_gacha_cnt + 1
|
||||
if gacha_count == 11
|
||||
else eleven_gacha_cnt,
|
||||
dailyGachaDate=daily_gacha_date,
|
||||
)
|
||||
|
||||
if "userData" in upsert and len(upsert["userData"]) > 0:
|
||||
# check if the profile is a bright memory profile
|
||||
p = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||
if p is not None:
|
||||
# save the bright memory profile
|
||||
self.data.profile.put_profile_data(
|
||||
user_id, self.version, upsert["userData"][0]
|
||||
)
|
||||
else:
|
||||
# save the bright profile
|
||||
self.data.profile.put_profile_data(
|
||||
user_id, self.version, upsert["userData"][0]
|
||||
)
|
||||
|
||||
if "userCharacterList" in upsert:
|
||||
for x in upsert["userCharacterList"]:
|
||||
self.data.item.put_character(user_id, x)
|
||||
|
||||
if "userItemList" in upsert:
|
||||
for x in upsert["userItemList"]:
|
||||
self.data.item.put_item(user_id, x)
|
||||
|
||||
if "userCardList" in upsert:
|
||||
for x in upsert["userCardList"]:
|
||||
self.data.item.put_card(user_id, x)
|
||||
|
||||
# TODO?
|
||||
# if "gameGachaCardList" in upsert:
|
||||
# for x in upsert["gameGachaCardList"]:
|
||||
|
||||
return {"returnCode": 1, "apiName": "CMUpsertUserGachaApi"}
|
||||
|
||||
def handle_cm_upsert_user_select_gacha_api_request(self, data: Dict) -> Dict:
|
||||
upsert = data["cmUpsertUserSelectGacha"]
|
||||
user_id = data["userId"]
|
||||
|
||||
if "userData" in upsert and len(upsert["userData"]) > 0:
|
||||
# check if the profile is a bright memory profile
|
||||
p = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||
if p is not None:
|
||||
# save the bright memory profile
|
||||
self.data.profile.put_profile_data(
|
||||
user_id, self.version, upsert["userData"][0]
|
||||
)
|
||||
else:
|
||||
# save the bright profile
|
||||
self.data.profile.put_profile_data(
|
||||
user_id, self.version, upsert["userData"][0]
|
||||
)
|
||||
|
||||
if "userCharacterList" in upsert:
|
||||
for x in upsert["userCharacterList"]:
|
||||
self.data.item.put_character(user_id, x)
|
||||
|
||||
if "userCardList" in upsert:
|
||||
for x in upsert["userCardList"]:
|
||||
self.data.item.put_card(user_id, x)
|
||||
|
||||
if "selectGachaLogList" in data:
|
||||
for x in data["selectGachaLogList"]:
|
||||
self.data.item.put_user_gacha(
|
||||
user_id,
|
||||
x["gachaId"],
|
||||
selectPoint=0,
|
||||
useSelectPoint=x["useSelectPoint"],
|
||||
)
|
||||
|
||||
return {"returnCode": 1, "apiName": "cmUpsertUserSelectGacha"}
|
||||
|
||||
def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict:
|
||||
game_gacha_cards = self.data.static.get_gacha_cards(data["gachaId"])
|
||||
if game_gacha_cards == []:
|
||||
# fallback to be at least able to select that gacha
|
||||
return {
|
||||
"gachaId": data["gachaId"],
|
||||
"length": 6,
|
||||
"isPickup": False,
|
||||
"gameGachaCardList": [
|
||||
{
|
||||
"gachaId": data["gachaId"],
|
||||
"cardId": 100984,
|
||||
"rarity": 4,
|
||||
"weight": 1,
|
||||
"isPickup": False,
|
||||
"isSelect": True,
|
||||
},
|
||||
{
|
||||
"gachaId": data["gachaId"],
|
||||
"cardId": 100997,
|
||||
"rarity": 3,
|
||||
"weight": 2,
|
||||
"isPickup": False,
|
||||
"isSelect": True,
|
||||
},
|
||||
{
|
||||
"gachaId": data["gachaId"],
|
||||
"cardId": 100998,
|
||||
"rarity": 3,
|
||||
"weight": 2,
|
||||
"isPickup": False,
|
||||
"isSelect": True,
|
||||
},
|
||||
{
|
||||
"gachaId": data["gachaId"],
|
||||
"cardId": 101020,
|
||||
"rarity": 2,
|
||||
"weight": 3,
|
||||
"isPickup": False,
|
||||
"isSelect": True,
|
||||
},
|
||||
{
|
||||
"gachaId": data["gachaId"],
|
||||
"cardId": 101021,
|
||||
"rarity": 2,
|
||||
"weight": 3,
|
||||
"isPickup": False,
|
||||
"isSelect": True,
|
||||
},
|
||||
{
|
||||
"gachaId": data["gachaId"],
|
||||
"cardId": 101022,
|
||||
"rarity": 2,
|
||||
"weight": 3,
|
||||
"isPickup": False,
|
||||
"isSelect": True,
|
||||
},
|
||||
],
|
||||
"emissionList": [],
|
||||
"afterCalcList": [],
|
||||
"ssrBookCalcList": [],
|
||||
}
|
||||
|
||||
game_gacha_card_list = []
|
||||
for gacha_card in game_gacha_cards:
|
||||
tmp = gacha_card._asdict()
|
||||
tmp.pop("id")
|
||||
game_gacha_card_list.append(tmp)
|
||||
|
||||
return {
|
||||
"gachaId": data["gachaId"],
|
||||
"length": len(game_gacha_card_list),
|
||||
"isPickup": False,
|
||||
"gameGachaCardList": game_gacha_card_list,
|
||||
# again no clue
|
||||
"emissionList": [],
|
||||
"afterCalcList": [],
|
||||
"ssrBookCalcList": [],
|
||||
}
|
||||
|
||||
def handle_get_game_theater_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
shows a banner after every print, not sure what its used for
|
||||
"""
|
||||
|
||||
"""
|
||||
return {
|
||||
"length": 1,
|
||||
"gameTheaterList": [{
|
||||
"theaterId": 1,
|
||||
"theaterName": "theaterName",
|
||||
"startDate": "2018-01-01 00:00:00.0",
|
||||
"endDate": "2038-01-01 00:00:00.0",
|
||||
"gameSubTheaterList": [{
|
||||
"theaterId": 1,
|
||||
"id": 2,
|
||||
"no": 4
|
||||
}]
|
||||
}
|
||||
],
|
||||
"registIdList": []
|
||||
}
|
||||
"""
|
||||
|
||||
return {"length": 0, "gameTheaterList": [], "registIdList": []}
|
||||
|
||||
def handle_cm_upsert_user_print_playlog_api_request(self, data: Dict) -> Dict:
|
||||
return {
|
||||
"returnCode": 1,
|
||||
"orderId": 0,
|
||||
"serialId": "11111111111111111111",
|
||||
"apiName": "CMUpsertUserPrintPlaylogApi",
|
||||
}
|
||||
|
||||
def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
|
||||
return {
|
||||
"returnCode": 1,
|
||||
"orderId": 0,
|
||||
"serialId": "11111111111111111111",
|
||||
"apiName": "CMUpsertUserPrintlogApi",
|
||||
}
|
||||
|
||||
def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
|
||||
user_print_detail = data["userPrintDetail"]
|
||||
|
||||
# generate random serial id
|
||||
serial_id = "".join([str(randint(0, 9)) for _ in range(20)])
|
||||
|
||||
# not needed because are either zero or unset
|
||||
user_print_detail.pop("orderId")
|
||||
user_print_detail.pop("printNumber")
|
||||
user_print_detail.pop("serialId")
|
||||
user_print_detail["printDate"] = datetime.strptime(
|
||||
user_print_detail["printDate"], "%Y-%m-%d"
|
||||
)
|
||||
|
||||
# add the entry to the user print table with the random serialId
|
||||
self.data.item.put_user_print_detail(
|
||||
data["userId"], serial_id, user_print_detail
|
||||
)
|
||||
|
||||
return {
|
||||
"returnCode": 1,
|
||||
"serialId": serial_id,
|
||||
"apiName": "CMUpsertUserPrintApi",
|
||||
}
|
||||
|
||||
def handle_cm_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
||||
upsert = data["cmUpsertUserAll"]
|
||||
user_id = data["userId"]
|
||||
|
||||
if "userData" in upsert and len(upsert["userData"]) > 0:
|
||||
# check if the profile is a bright memory profile
|
||||
p = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||
if p is not None:
|
||||
# save the bright memory profile
|
||||
self.data.profile.put_profile_data(
|
||||
user_id, self.version, upsert["userData"][0]
|
||||
)
|
||||
else:
|
||||
# save the bright profile
|
||||
self.data.profile.put_profile_data(
|
||||
user_id, self.version, upsert["userData"][0]
|
||||
)
|
||||
|
||||
if "userActivityList" in upsert:
|
||||
for act in upsert["userActivityList"]:
|
||||
self.data.profile.put_profile_activity(
|
||||
user_id,
|
||||
act["kind"],
|
||||
act["id"],
|
||||
act["sortNumber"],
|
||||
act["param1"],
|
||||
act["param2"],
|
||||
act["param3"],
|
||||
act["param4"],
|
||||
)
|
||||
|
||||
if "userItemList" in upsert:
|
||||
for x in upsert["userItemList"]:
|
||||
self.data.item.put_item(user_id, x)
|
||||
|
||||
if "userCardList" in upsert:
|
||||
for x in upsert["userCardList"]:
|
||||
self.data.item.put_card(user_id, x)
|
||||
|
||||
return {"returnCode": 1, "apiName": "cmUpsertUserAll"}
|
||||
|
||||
149
titles/ongeki/brightmemory.py
Normal file
149
titles/ongeki/brightmemory.py
Normal file
@@ -0,0 +1,149 @@
|
||||
from datetime import date, datetime, timedelta
|
||||
from typing import Any, Dict
|
||||
import pytz
|
||||
import json
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.ongeki.base import OngekiBase
|
||||
from titles.ongeki.bright import OngekiBright
|
||||
from titles.ongeki.const import OngekiConstants
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
|
||||
|
||||
class OngekiBrightMemory(OngekiBright):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.35.00"
|
||||
ret["gameSetting"]["maxCountCharacter"] = 50
|
||||
ret["gameSetting"]["maxCountCard"] = 300
|
||||
ret["gameSetting"]["maxCountItem"] = 300
|
||||
ret["gameSetting"]["maxCountMusic"] = 50
|
||||
ret["gameSetting"]["maxCountMusicItem"] = 300
|
||||
ret["gameSetting"]["maxCountRivalMusic"] = 300
|
||||
return ret
|
||||
|
||||
def handle_get_user_memory_chapter_api_request(self, data: Dict) -> Dict:
|
||||
memories = self.data.item.get_memorychapters(data["userId"])
|
||||
if not memories:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": 6,
|
||||
"userMemoryChapterList": [
|
||||
{
|
||||
"gaugeId": 0,
|
||||
"isClear": False,
|
||||
"gaugeNum": 0,
|
||||
"chapterId": 70001,
|
||||
"jewelCount": 0,
|
||||
"isBossWatched": False,
|
||||
"isStoryWatched": False,
|
||||
"isDialogWatched": False,
|
||||
"isEndingWatched": False,
|
||||
"lastPlayMusicId": 0,
|
||||
"lastPlayMusicLevel": 0,
|
||||
"lastPlayMusicCategory": 0,
|
||||
},
|
||||
{
|
||||
"gaugeId": 0,
|
||||
"isClear": False,
|
||||
"gaugeNum": 0,
|
||||
"chapterId": 70002,
|
||||
"jewelCount": 0,
|
||||
"isBossWatched": False,
|
||||
"isStoryWatched": False,
|
||||
"isDialogWatched": False,
|
||||
"isEndingWatched": False,
|
||||
"lastPlayMusicId": 0,
|
||||
"lastPlayMusicLevel": 0,
|
||||
"lastPlayMusicCategory": 0,
|
||||
},
|
||||
{
|
||||
"gaugeId": 0,
|
||||
"isClear": False,
|
||||
"gaugeNum": 0,
|
||||
"chapterId": 70003,
|
||||
"jewelCount": 0,
|
||||
"isBossWatched": False,
|
||||
"isStoryWatched": False,
|
||||
"isDialogWatched": False,
|
||||
"isEndingWatched": False,
|
||||
"lastPlayMusicId": 0,
|
||||
"lastPlayMusicLevel": 0,
|
||||
"lastPlayMusicCategory": 0,
|
||||
},
|
||||
{
|
||||
"gaugeId": 0,
|
||||
"isClear": False,
|
||||
"gaugeNum": 0,
|
||||
"chapterId": 70004,
|
||||
"jewelCount": 0,
|
||||
"isBossWatched": False,
|
||||
"isStoryWatched": False,
|
||||
"isDialogWatched": False,
|
||||
"isEndingWatched": False,
|
||||
"lastPlayMusicId": 0,
|
||||
"lastPlayMusicLevel": 0,
|
||||
"lastPlayMusicCategory": 0,
|
||||
},
|
||||
{
|
||||
"gaugeId": 0,
|
||||
"isClear": False,
|
||||
"gaugeNum": 0,
|
||||
"chapterId": 70005,
|
||||
"jewelCount": 0,
|
||||
"isBossWatched": False,
|
||||
"isStoryWatched": False,
|
||||
"isDialogWatched": False,
|
||||
"isEndingWatched": False,
|
||||
"lastPlayMusicId": 0,
|
||||
"lastPlayMusicLevel": 0,
|
||||
"lastPlayMusicCategory": 0,
|
||||
},
|
||||
{
|
||||
"gaugeId": 0,
|
||||
"isClear": False,
|
||||
"gaugeNum": 0,
|
||||
"chapterId": 70099,
|
||||
"jewelCount": 0,
|
||||
"isBossWatched": False,
|
||||
"isStoryWatched": False,
|
||||
"isDialogWatched": False,
|
||||
"isEndingWatched": False,
|
||||
"lastPlayMusicId": 0,
|
||||
"lastPlayMusicLevel": 0,
|
||||
"lastPlayMusicCategory": 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
memory_chp = []
|
||||
for chp in memories:
|
||||
tmp = chp._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
memory_chp.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(memory_chp),
|
||||
"userMemoryChapterList": memory_chp,
|
||||
}
|
||||
|
||||
def handle_get_game_music_release_state_api_request(self, data: Dict) -> Dict:
|
||||
return {"techScore": 0, "cardNum": 0}
|
||||
|
||||
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||
# check for a bright memory profile
|
||||
user_data = super().handle_cm_get_user_data_api_request(data)
|
||||
|
||||
# hardcode Card Maker version for now
|
||||
# Card Maker 1.34.00 = 1.30.01
|
||||
# Card Maker 1.36.00 = 1.35.04
|
||||
user_data["userData"]["compatibleCmVersion"] = "1.35.04"
|
||||
|
||||
return user_data
|
||||
@@ -1,17 +1,39 @@
|
||||
from typing import List
|
||||
|
||||
from core.config import CoreConfig
|
||||
|
||||
class OngekiServerConfig():
|
||||
|
||||
class OngekiServerConfig:
|
||||
def __init__(self, parent_config: "OngekiConfig") -> None:
|
||||
self.__config = parent_config
|
||||
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(self.__config, 'ongeki', 'server', 'enable', default=True)
|
||||
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "ongeki", "server", "enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def loglevel(self) -> int:
|
||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'ongeki', 'server', 'loglevel', default="info"))
|
||||
return CoreConfig.str_to_loglevel(
|
||||
CoreConfig.get_config_field(
|
||||
self.__config, "ongeki", "server", "loglevel", default="info"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class OngekiGachaConfig:
|
||||
def __init__(self, parent_config: "OngekiConfig") -> None:
|
||||
self.__config = parent_config
|
||||
|
||||
@property
|
||||
def enabled_gachas(self) -> List[int]:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "ongeki", "gachas", "enabled_gachas", default=[]
|
||||
)
|
||||
|
||||
|
||||
class OngekiConfig(dict):
|
||||
def __init__(self) -> None:
|
||||
self.server = OngekiServerConfig(self)
|
||||
self.gachas = OngekiGachaConfig(self)
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
from typing import Final, Dict
|
||||
from enum import Enum
|
||||
class OngekiConstants():
|
||||
|
||||
|
||||
class OngekiConstants:
|
||||
GAME_CODE = "SDDT"
|
||||
|
||||
CONFIG_NAME = "ongeki.yaml"
|
||||
|
||||
VER_ONGEKI = 0
|
||||
VER_ONGEKI_PLUS = 1
|
||||
VER_ONGEKI_SUMMER = 2
|
||||
@@ -10,29 +14,47 @@ class OngekiConstants():
|
||||
VER_ONGEKI_RED = 4
|
||||
VER_ONGEKI_RED_PLUS = 5
|
||||
VER_ONGEKI_BRIGHT = 6
|
||||
VER_ONGEKI_BRIGHT_MEMORY = 7
|
||||
|
||||
EVT_TYPES: Enum = Enum('EVT_TYPES', [
|
||||
'None',
|
||||
'Announcement',
|
||||
'Movie',
|
||||
'AddMyList',
|
||||
'UnlockChapter',
|
||||
'JewelEvent',
|
||||
'RankingEvent',
|
||||
'AcceptRankingEvent',
|
||||
'UnlockMusic',
|
||||
'UnlockCard',
|
||||
'UnlockTrophy',
|
||||
'UnlockNamePlate',
|
||||
'UnlockLimitBreakItem',
|
||||
'MissionEvent',
|
||||
'DailyBonus',
|
||||
'UnlockBossLockEarly',
|
||||
'UnlockPurchaseItem',
|
||||
'TechChallengeEvent',
|
||||
'AcceptTechChallengeEvent',
|
||||
'SilverJewelEvent',
|
||||
])
|
||||
EVT_TYPES: Enum = Enum(
|
||||
"EVT_TYPES",
|
||||
[
|
||||
"None",
|
||||
"Announcement",
|
||||
"Movie",
|
||||
"AddMyList",
|
||||
"UnlockChapter",
|
||||
"JewelEvent",
|
||||
"RankingEvent",
|
||||
"AcceptRankingEvent",
|
||||
"UnlockMusic",
|
||||
"UnlockCard",
|
||||
"UnlockTrophy",
|
||||
"UnlockNamePlate",
|
||||
"UnlockLimitBreakItem",
|
||||
"MissionEvent",
|
||||
"DailyBonus",
|
||||
"UnlockBossLockEarly",
|
||||
"UnlockPurchaseItem",
|
||||
"TechChallengeEvent",
|
||||
"AcceptTechChallengeEvent",
|
||||
"SilverJewelEvent",
|
||||
],
|
||||
)
|
||||
|
||||
class CM_GACHA_KINDS(Enum):
|
||||
Normal = 0
|
||||
Pickup = 1
|
||||
BonusRestored = 2
|
||||
Free = 3
|
||||
PickupBonusRestored = 4
|
||||
|
||||
class RARITY_TYPES(Enum):
|
||||
N = 0
|
||||
R = 1
|
||||
SR = 2
|
||||
SSR = 3
|
||||
SRPlus = 12
|
||||
|
||||
# The game expects the server to give Lunatic an ID of 10, while the game uses 4 internally... except in Music.xml
|
||||
class DIFF_NAME(Enum):
|
||||
@@ -42,9 +64,17 @@ class OngekiConstants():
|
||||
Master = 3
|
||||
Lunatic = 10
|
||||
|
||||
VERSION_NAMES = ("ONGEKI", "ONGEKI+", "ONGEKI Summer", "ONGEKI Summer+", "ONGEKI Red", "ONGEKI Red+",
|
||||
"ONGEKI Bright")
|
||||
VERSION_NAMES = (
|
||||
"ONGEKI",
|
||||
"ONGEKI+",
|
||||
"ONGEKI Summer",
|
||||
"ONGEKI Summer+",
|
||||
"ONGEKI Red",
|
||||
"ONGEKI Red+",
|
||||
"ONGEKI Bright",
|
||||
"ONGEKI Bright Memory",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def game_ver_to_string(cls, ver: int):
|
||||
return cls.VERSION_NAMES[ver]
|
||||
return cls.VERSION_NAMES[ver]
|
||||
|
||||
@@ -3,6 +3,7 @@ from core.config import CoreConfig
|
||||
from titles.ongeki.schema import OngekiItemData, OngekiProfileData, OngekiScoreData
|
||||
from titles.ongeki.schema import OngekiStaticData, OngekiLogData
|
||||
|
||||
|
||||
class OngekiData(Data):
|
||||
def __init__(self, cfg: CoreConfig) -> None:
|
||||
super().__init__(cfg)
|
||||
@@ -11,4 +12,4 @@ class OngekiData(Data):
|
||||
self.profile = OngekiProfileData(cfg, self.session)
|
||||
self.score = OngekiScoreData(cfg, self.session)
|
||||
self.static = OngekiStaticData(cfg, self.session)
|
||||
self.log = OngekiLogData(cfg, self.session)
|
||||
self.log = OngekiLogData(cfg, self.session)
|
||||
|
||||
@@ -6,6 +6,8 @@ import string
|
||||
import logging, coloredlogs
|
||||
import zlib
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from os import path
|
||||
from typing import Tuple
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
@@ -17,12 +19,17 @@ from titles.ongeki.summerplus import OngekiSummerPlus
|
||||
from titles.ongeki.red import OngekiRed
|
||||
from titles.ongeki.redplus import OngekiRedPlus
|
||||
from titles.ongeki.bright import OngekiBright
|
||||
from titles.ongeki.brightmemory import OngekiBrightMemory
|
||||
|
||||
class OngekiServlet():
|
||||
|
||||
class OngekiServlet:
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.game_cfg = OngekiConfig()
|
||||
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/ongeki.yaml")))
|
||||
if path.exists(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"):
|
||||
self.game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
self.versions = [
|
||||
OngekiBase(core_cfg, self.game_cfg),
|
||||
@@ -32,83 +39,122 @@ class OngekiServlet():
|
||||
OngekiRed(core_cfg, self.game_cfg),
|
||||
OngekiRedPlus(core_cfg, self.game_cfg),
|
||||
OngekiBright(core_cfg, self.game_cfg),
|
||||
OngekiBrightMemory(core_cfg, self.game_cfg),
|
||||
]
|
||||
|
||||
self.logger = logging.getLogger("ongeki")
|
||||
log_fmt_str = "[%(asctime)s] Ongeki | %(levelname)s | %(message)s"
|
||||
log_fmt = logging.Formatter(log_fmt_str)
|
||||
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "ongeki"), encoding='utf8',
|
||||
when="d", backupCount=10)
|
||||
fileHandler = TimedRotatingFileHandler(
|
||||
"{0}/{1}.log".format(self.core_cfg.server.log_dir, "ongeki"),
|
||||
encoding="utf8",
|
||||
when="d",
|
||||
backupCount=10,
|
||||
)
|
||||
|
||||
fileHandler.setFormatter(log_fmt)
|
||||
|
||||
|
||||
consoleHandler = logging.StreamHandler()
|
||||
consoleHandler.setFormatter(log_fmt)
|
||||
|
||||
self.logger.addHandler(fileHandler)
|
||||
self.logger.addHandler(consoleHandler)
|
||||
|
||||
|
||||
self.logger.setLevel(self.game_cfg.server.loglevel)
|
||||
coloredlogs.install(level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str)
|
||||
coloredlogs.install(
|
||||
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_allnet_info(
|
||||
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
|
||||
) -> Tuple[bool, str, str]:
|
||||
game_cfg = OngekiConfig()
|
||||
|
||||
if path.exists(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"):
|
||||
game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
if not game_cfg.server.enable:
|
||||
return (False, "", "")
|
||||
|
||||
if core_cfg.server.is_develop:
|
||||
return (
|
||||
True,
|
||||
f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/",
|
||||
f"{core_cfg.title.hostname}:{core_cfg.title.port}/",
|
||||
)
|
||||
|
||||
return (
|
||||
True,
|
||||
f"http://{core_cfg.title.hostname}/{game_code}/$v/",
|
||||
f"{core_cfg.title.hostname}/",
|
||||
)
|
||||
|
||||
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
|
||||
if url_path.lower() == "/ping":
|
||||
return zlib.compress(b'{"returnCode": 1}')
|
||||
|
||||
req_raw = request.content.getvalue()
|
||||
url_split = url_path.split("/")
|
||||
internal_ver = 0
|
||||
endpoint = url_split[len(url_split) - 1]
|
||||
|
||||
if version < 105: # 1.0
|
||||
if version < 105: # 1.0
|
||||
internal_ver = OngekiConstants.VER_ONGEKI
|
||||
elif version >= 105 and version < 110: # Plus
|
||||
elif version >= 105 and version < 110: # Plus
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_PLUS
|
||||
elif version >= 110 and version < 115: # Summer
|
||||
elif version >= 110 and version < 115: # Summer
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_SUMMER
|
||||
elif version >= 115 and version < 120: # Summer Plus
|
||||
elif version >= 115 and version < 120: # Summer Plus
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_SUMMER_PLUS
|
||||
elif version >= 120 and version < 125: # Red
|
||||
elif version >= 120 and version < 125: # Red
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_RED
|
||||
elif version >= 125 and version < 130: # Red Plus
|
||||
elif version >= 125 and version < 130: # Red Plus
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_RED_PLUS
|
||||
elif version >= 130 and version < 135: # Red Plus
|
||||
elif version >= 130 and version < 135: # Bright
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_BRIGHT
|
||||
elif version >= 135 and version < 140: # Bright Memory
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
||||
|
||||
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
|
||||
# doing encrypted. The likelyhood of false positives is low but
|
||||
# If we get a 32 character long hex string, it's a hash and we're
|
||||
# doing encrypted. The likelyhood of false positives is low but
|
||||
# technically not 0
|
||||
self.logger.error("Encryption not supported at this time")
|
||||
return b""
|
||||
|
||||
try:
|
||||
try:
|
||||
unzip = zlib.decompress(req_raw)
|
||||
|
||||
|
||||
except zlib.error as e:
|
||||
self.logger.error(f"Failed to decompress v{version} {endpoint} request -> {e}")
|
||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||
|
||||
self.logger.error(
|
||||
f"Failed to decompress v{version} {endpoint} request -> {e}"
|
||||
)
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
|
||||
req_data = json.loads(unzip)
|
||||
|
||||
|
||||
self.logger.info(f"v{version} {endpoint} request - {req_data}")
|
||||
|
||||
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
||||
|
||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||
return zlib.compress(b'{"returnCode": 1}')
|
||||
|
||||
try:
|
||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||
resp = handler(req_data)
|
||||
|
||||
except AttributeError as e:
|
||||
self.logger.warning(f"Unhandled v{version} request {endpoint} - {e}")
|
||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
|
||||
if resp == None:
|
||||
resp = {'returnCode': 1}
|
||||
|
||||
resp = {"returnCode": 1}
|
||||
|
||||
self.logger.info(f"Response {resp}")
|
||||
|
||||
|
||||
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@ from titles.ongeki.base import OngekiBase
|
||||
from titles.ongeki.const import OngekiConstants
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
|
||||
|
||||
class OngekiPlus(OngekiBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_PLUS
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.05.00"
|
||||
|
||||
@@ -11,29 +11,111 @@ from titles.ongeki.database import OngekiData
|
||||
from titles.ongeki.const import OngekiConstants
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
|
||||
|
||||
class OngekiReader(BaseReader):
|
||||
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str], opt_dir: Optional[str], extra: Optional[str]) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
config: CoreConfig,
|
||||
version: int,
|
||||
bin_dir: Optional[str],
|
||||
opt_dir: Optional[str],
|
||||
extra: Optional[str],
|
||||
) -> None:
|
||||
super().__init__(config, version, bin_dir, opt_dir, extra)
|
||||
self.data = OngekiData(config)
|
||||
|
||||
try:
|
||||
self.logger.info(f"Start importer for {OngekiConstants.game_ver_to_string(version)}")
|
||||
self.logger.info(
|
||||
f"Start importer for {OngekiConstants.game_ver_to_string(version)}"
|
||||
)
|
||||
except IndexError:
|
||||
self.logger.error(f"Invalid ongeki version {version}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def read(self) -> None:
|
||||
data_dirs = []
|
||||
if self.bin_dir is not None:
|
||||
data_dirs += self.get_data_directories(self.bin_dir)
|
||||
|
||||
|
||||
if self.opt_dir is not None:
|
||||
data_dirs += self.get_data_directories(self.opt_dir)
|
||||
|
||||
|
||||
for dir in data_dirs:
|
||||
self.read_events(f"{dir}/event")
|
||||
self.read_music(f"{dir}/music")
|
||||
|
||||
self.read_card(f"{dir}/card")
|
||||
|
||||
def read_card(self, base_dir: str) -> None:
|
||||
self.logger.info(f"Reading cards from {base_dir}...")
|
||||
|
||||
version_ids = {
|
||||
"1000": OngekiConstants.VER_ONGEKI,
|
||||
"1005": OngekiConstants.VER_ONGEKI_PLUS,
|
||||
"1010": OngekiConstants.VER_ONGEKI_SUMMER,
|
||||
"1015": OngekiConstants.VER_ONGEKI_SUMMER_PLUS,
|
||||
"1020": OngekiConstants.VER_ONGEKI_RED,
|
||||
"1025": OngekiConstants.VER_ONGEKI_RED_PLUS,
|
||||
"1030": OngekiConstants.VER_ONGEKI_BRIGHT,
|
||||
"1035": OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY,
|
||||
}
|
||||
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
for dir in dirs:
|
||||
if os.path.exists(f"{root}/{dir}/Card.xml"):
|
||||
with open(f"{root}/{dir}/Card.xml", "r", encoding="utf-8") as f:
|
||||
troot = ET.fromstring(f.read())
|
||||
|
||||
card_id = int(troot.find("Name").find("id").text)
|
||||
|
||||
# skip already existing cards
|
||||
if (
|
||||
self.data.static.get_card(
|
||||
OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY, card_id
|
||||
)
|
||||
is not None
|
||||
):
|
||||
self.logger.info(f"Card {card_id} already added, skipping")
|
||||
continue
|
||||
|
||||
name = troot.find("Name").find("str").text
|
||||
chara_id = int(troot.find("CharaID").find("id").text)
|
||||
nick_name = troot.find("NickName").text
|
||||
school = troot.find("School").find("str").text
|
||||
attribute = troot.find("Attribute").text
|
||||
gakunen = troot.find("Gakunen").find("str").text
|
||||
rarity = OngekiConstants.RARITY_TYPES[
|
||||
troot.find("Rarity").text
|
||||
].value
|
||||
|
||||
level_param = []
|
||||
for lvl in troot.find("LevelParam").findall("int"):
|
||||
level_param.append(lvl.text)
|
||||
|
||||
skill_id = int(troot.find("SkillID").find("id").text)
|
||||
cho_kai_ka_skill_id = int(
|
||||
troot.find("ChoKaikaSkillID").find("id").text
|
||||
)
|
||||
|
||||
version = version_ids[troot.find("VersionID").find("id").text]
|
||||
card_number = troot.find("CardNumberString").text
|
||||
|
||||
self.data.static.put_card(
|
||||
version,
|
||||
card_id,
|
||||
name=name,
|
||||
charaId=chara_id,
|
||||
nickName=nick_name,
|
||||
school=school,
|
||||
attribute=attribute,
|
||||
gakunen=gakunen,
|
||||
rarity=rarity,
|
||||
levelParam=",".join(level_param),
|
||||
skillId=skill_id,
|
||||
choKaikaSkillId=cho_kai_ka_skill_id,
|
||||
cardNumber=card_number,
|
||||
)
|
||||
self.logger.info(f"Added card {card_id}")
|
||||
|
||||
def read_events(self, base_dir: str) -> None:
|
||||
self.logger.info(f"Reading events from {base_dir}...")
|
||||
|
||||
@@ -43,14 +125,15 @@ class OngekiReader(BaseReader):
|
||||
with open(f"{root}/{dir}/Event.xml", "r", encoding="utf-8") as f:
|
||||
troot = ET.fromstring(f.read())
|
||||
|
||||
name = troot.find('Name').find('str').text
|
||||
id = int(troot.find('Name').find('id').text)
|
||||
event_type = OngekiConstants.EVT_TYPES[troot.find('EventType').text].value
|
||||
|
||||
name = troot.find("Name").find("str").text
|
||||
id = int(troot.find("Name").find("id").text)
|
||||
event_type = OngekiConstants.EVT_TYPES[
|
||||
troot.find("EventType").text
|
||||
].value
|
||||
|
||||
self.data.static.put_event(self.version, id, event_type, name)
|
||||
self.logger.info(f"Added event {id}")
|
||||
|
||||
|
||||
def read_music(self, base_dir: str) -> None:
|
||||
self.logger.info(f"Reading music from {base_dir}...")
|
||||
|
||||
@@ -67,23 +150,24 @@ class OngekiReader(BaseReader):
|
||||
if root is None:
|
||||
continue
|
||||
|
||||
name = troot.find('Name')
|
||||
song_id = name.find('id').text
|
||||
title = name.find('str').text
|
||||
artist = troot.find('ArtistName').find('str').text
|
||||
genre = troot.find('Genre').find('str').text
|
||||
|
||||
name = troot.find("Name")
|
||||
song_id = name.find("id").text
|
||||
title = name.find("str").text
|
||||
artist = troot.find("ArtistName").find("str").text
|
||||
genre = troot.find("Genre").find("str").text
|
||||
|
||||
fumens = troot.find("FumenData")
|
||||
for fumens_data in fumens.findall('FumenData'):
|
||||
path = fumens_data.find('FumenFile').find('path').text
|
||||
for fumens_data in fumens.findall("FumenData"):
|
||||
path = fumens_data.find("FumenFile").find("path").text
|
||||
if path is None or not os.path.exists(f"{root}/{dir}/{path}"):
|
||||
continue
|
||||
|
||||
chart_id = int(path.split(".")[0].split("_")[1])
|
||||
level = float(
|
||||
f"{fumens_data.find('FumenConstIntegerPart').text}.{fumens_data.find('FumenConstFractionalPart').text}"
|
||||
)
|
||||
|
||||
self.data.static.put_chart(self.version, song_id, chart_id, title, artist, genre, level)
|
||||
self.logger.info(f"Added song {song_id} chart {chart_id}")
|
||||
)
|
||||
|
||||
self.data.static.put_chart(
|
||||
self.version, song_id, chart_id, title, artist, genre, level
|
||||
)
|
||||
self.logger.info(f"Added song {song_id} chart {chart_id}")
|
||||
|
||||
@@ -5,11 +5,12 @@ from titles.ongeki.base import OngekiBase
|
||||
from titles.ongeki.const import OngekiConstants
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
|
||||
|
||||
class OngekiRed(OngekiBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_RED
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.20.00"
|
||||
|
||||
@@ -5,11 +5,12 @@ from titles.ongeki.base import OngekiBase
|
||||
from titles.ongeki.const import OngekiConstants
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
|
||||
|
||||
class OngekiRedPlus(OngekiBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_RED_PLUS
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.25.00"
|
||||
|
||||
@@ -4,4 +4,10 @@ from titles.ongeki.schema.static import OngekiStaticData
|
||||
from titles.ongeki.schema.score import OngekiScoreData
|
||||
from titles.ongeki.schema.log import OngekiLogData
|
||||
|
||||
__all__ = [OngekiProfileData, OngekiItemData, OngekiStaticData, OngekiScoreData, OngekiLogData]
|
||||
__all__ = [
|
||||
OngekiProfileData,
|
||||
OngekiItemData,
|
||||
OngekiStaticData,
|
||||
OngekiScoreData,
|
||||
OngekiLogData,
|
||||
]
|
||||
|
||||
@@ -2,6 +2,7 @@ from typing import Dict, Optional, List
|
||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
@@ -27,7 +28,7 @@ card = Table(
|
||||
Column("isAcquired", Boolean),
|
||||
Column("created", String(25)),
|
||||
UniqueConstraint("user", "cardId", name="ongeki_user_card_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
deck = Table(
|
||||
@@ -40,7 +41,7 @@ deck = Table(
|
||||
Column("cardId2", Integer),
|
||||
Column("cardId3", Integer),
|
||||
UniqueConstraint("user", "deckId", name="ongeki_user_deck_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
character = Table(
|
||||
@@ -58,10 +59,10 @@ character = Table(
|
||||
Column("intimateCountDate", String(25)),
|
||||
Column("isNew", Boolean),
|
||||
UniqueConstraint("user", "characterId", name="ongeki_user_character_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
boss = Table (
|
||||
boss = Table(
|
||||
"ongeki_user_boss",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
@@ -71,10 +72,10 @@ boss = Table (
|
||||
Column("isClear", Boolean),
|
||||
Column("eventId", Integer),
|
||||
UniqueConstraint("user", "musicId", "eventId", name="ongeki_user_boss_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
story = Table (
|
||||
story = Table(
|
||||
"ongeki_user_story",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
@@ -86,7 +87,7 @@ story = Table (
|
||||
Column("lastPlayMusicCategory", Integer),
|
||||
Column("lastPlayMusicLevel", Integer),
|
||||
UniqueConstraint("user", "storyId", name="ongeki_user_story_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
chapter = Table(
|
||||
@@ -104,7 +105,28 @@ chapter = Table(
|
||||
Column("skipTiming1", Integer),
|
||||
Column("skipTiming2", Integer),
|
||||
UniqueConstraint("user", "chapterId", name="ongeki_user_chapter_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
memorychapter = Table(
|
||||
"ongeki_user_memorychapter",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("chapterId", Integer),
|
||||
Column("gaugeId", Integer),
|
||||
Column("gaugeNum", Integer),
|
||||
Column("jewelCount", Integer),
|
||||
Column("isStoryWatched", Boolean),
|
||||
Column("isBossWatched", Boolean),
|
||||
Column("isDialogWatched", Boolean),
|
||||
Column("isEndingWatched", Boolean),
|
||||
Column("isClear", Boolean),
|
||||
Column("lastPlayMusicId", Integer),
|
||||
Column("lastPlayMusicLevel", Integer),
|
||||
Column("lastPlayMusicCategory", Integer),
|
||||
UniqueConstraint("user", "chapterId", name="ongeki_user_memorychapter_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
item = Table(
|
||||
@@ -117,7 +139,7 @@ item = Table(
|
||||
Column("stock", Integer),
|
||||
Column("isValid", Boolean),
|
||||
UniqueConstraint("user", "itemKind", "itemId", name="ongeki_user_item_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
music_item = Table(
|
||||
@@ -128,7 +150,7 @@ music_item = Table(
|
||||
Column("musicId", Integer),
|
||||
Column("status", Integer),
|
||||
UniqueConstraint("user", "musicId", name="ongeki_user_music_item_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
login_bonus = Table(
|
||||
@@ -140,7 +162,7 @@ login_bonus = Table(
|
||||
Column("bonusCount", Integer),
|
||||
Column("lastUpdateDate", String(25)),
|
||||
UniqueConstraint("user", "bonusId", name="ongeki_user_login_bonus_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
event_point = Table(
|
||||
@@ -152,7 +174,7 @@ event_point = Table(
|
||||
Column("point", Integer),
|
||||
Column("isRankingRewarded", Boolean),
|
||||
UniqueConstraint("user", "eventId", name="ongeki_user_event_point_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
mission_point = Table(
|
||||
@@ -163,7 +185,7 @@ mission_point = Table(
|
||||
Column("eventId", Integer),
|
||||
Column("point", Integer),
|
||||
UniqueConstraint("user", "eventId", name="ongeki_user_mission_point_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
scenerio = Table(
|
||||
@@ -174,7 +196,7 @@ scenerio = Table(
|
||||
Column("scenarioId", Integer),
|
||||
Column("playCount", Integer),
|
||||
UniqueConstraint("user", "scenarioId", name="ongeki_user_scenerio_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
trade_item = Table(
|
||||
@@ -185,8 +207,10 @@ trade_item = Table(
|
||||
Column("chapterId", Integer),
|
||||
Column("tradeItemId", Integer),
|
||||
Column("tradeCount", Integer),
|
||||
UniqueConstraint("user", "chapterId", "tradeItemId", name="ongeki_user_trade_item_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
UniqueConstraint(
|
||||
"user", "chapterId", "tradeItemId", name="ongeki_user_trade_item_uk"
|
||||
),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
event_music = Table(
|
||||
@@ -202,8 +226,10 @@ event_music = Table(
|
||||
Column("platinumScoreMax", Integer),
|
||||
Column("techRecordDate", String(25)),
|
||||
Column("isTechNewRecord", Boolean),
|
||||
UniqueConstraint("user", "eventId", "type", "musicId", "level", name="ongeki_user_event_music"),
|
||||
mysql_charset='utf8mb4'
|
||||
UniqueConstraint(
|
||||
"user", "eventId", "type", "musicId", "level", name="ongeki_user_event_music"
|
||||
),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
tech_event = Table(
|
||||
@@ -218,11 +244,80 @@ tech_event = Table(
|
||||
Column("isRankingRewarded", Boolean),
|
||||
Column("isTotalTechNewRecord", Boolean),
|
||||
UniqueConstraint("user", "eventId", name="ongeki_user_tech_event_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
gacha = Table(
|
||||
"ongeki_user_gacha",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("gachaId", Integer, nullable=False),
|
||||
Column("totalGachaCnt", Integer, server_default="0"),
|
||||
Column("ceilingGachaCnt", Integer, server_default="0"),
|
||||
Column("selectPoint", Integer, server_default="0"),
|
||||
Column("useSelectPoint", Integer, server_default="0"),
|
||||
Column("dailyGachaCnt", Integer, server_default="0"),
|
||||
Column("fiveGachaCnt", Integer, server_default="0"),
|
||||
Column("elevenGachaCnt", Integer, server_default="0"),
|
||||
Column("dailyGachaDate", TIMESTAMP, nullable=False, server_default=func.now()),
|
||||
UniqueConstraint("user", "gachaId", name="ongeki_user_gacha_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
gacha_supply = Table(
|
||||
"ongeki_user_gacha_supply",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("cardId", Integer, nullable=False),
|
||||
UniqueConstraint("user", "cardId", name="ongeki_user_gacha_supply_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class OngekiItemData(BaseData):
|
||||
print_detail = Table(
|
||||
"ongeki_user_print_detail",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("cardId", Integer, nullable=False),
|
||||
Column("cardType", Integer, server_default="0"),
|
||||
Column("printDate", TIMESTAMP, nullable=False),
|
||||
Column("serialId", String(20), nullable=False),
|
||||
Column("placeId", Integer, nullable=False),
|
||||
Column("clientId", String(11), nullable=False),
|
||||
Column("printerSerialId", String(20), nullable=False),
|
||||
Column("isHolograph", Boolean, server_default="0"),
|
||||
Column("isAutographed", Boolean, server_default="0"),
|
||||
Column("printOption1", Boolean, server_default="1"),
|
||||
Column("printOption2", Boolean, server_default="1"),
|
||||
Column("printOption3", Boolean, server_default="1"),
|
||||
Column("printOption4", Boolean, server_default="1"),
|
||||
Column("printOption5", Boolean, server_default="1"),
|
||||
Column("printOption6", Boolean, server_default="1"),
|
||||
Column("printOption7", Boolean, server_default="1"),
|
||||
Column("printOption8", Boolean, server_default="1"),
|
||||
Column("printOption9", Boolean, server_default="1"),
|
||||
Column("printOption10", Boolean, server_default="0"),
|
||||
UniqueConstraint("serialId", name="ongeki_user_print_detail_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class OngekiItemData(BaseData):
|
||||
def put_card(self, aime_id: int, card_data: Dict) -> Optional[int]:
|
||||
card_data["user"] = aime_id
|
||||
|
||||
@@ -239,7 +334,8 @@ class OngekiItemData(BaseData):
|
||||
sql = select(card).where(card.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_character(self, aime_id: int, character_data: Dict) -> Optional[int]:
|
||||
@@ -253,12 +349,13 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_character: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_characters(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(character).where(character.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_deck(self, aime_id: int, deck_data: Dict) -> Optional[int]:
|
||||
@@ -272,19 +369,21 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_deck: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_deck(self, aime_id: int, deck_id: int) -> Optional[Dict]:
|
||||
sql = select(deck).where(and_(deck.c.user == aime_id, deck.c.deckId == deck_id))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
|
||||
def get_decks(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(deck).where(deck.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_boss(self, aime_id: int, boss_data: Dict) -> Optional[int]:
|
||||
@@ -298,7 +397,7 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_boss: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def put_story(self, aime_id: int, story_data: Dict) -> Optional[int]:
|
||||
story_data["user"] = aime_id
|
||||
|
||||
@@ -310,12 +409,13 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_story: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_stories(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(story).where(story.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_chapter(self, aime_id: int, chapter_data: Dict) -> Optional[int]:
|
||||
@@ -329,12 +429,13 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_chapter: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_chapters(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(chapter).where(chapter.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_item(self, aime_id: int, item_data: Dict) -> Optional[int]:
|
||||
@@ -348,22 +449,26 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_item: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_item(self, aime_id: int, item_id: int, item_kind: int) -> Optional[Dict]:
|
||||
sql = select(item).where(and_(item.c.user == aime_id, item.c.itemId == item_id))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_items(self, aime_id: int, item_kind: int = None) -> Optional[List[Dict]]:
|
||||
if item_kind is None:
|
||||
sql = select(item).where(item.c.user == aime_id)
|
||||
else:
|
||||
sql = select(item).where(and_(item.c.user == aime_id, item.c.itemKind == item_kind))
|
||||
sql = select(item).where(
|
||||
and_(item.c.user == aime_id, item.c.itemKind == item_kind)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_music_item(self, aime_id: int, music_item_data: Dict) -> Optional[int]:
|
||||
@@ -377,12 +482,13 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_music_item: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_music_items(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(music_item).where(music_item.c.user == aime_id)
|
||||
result = self.execute(sql)
|
||||
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_login_bonus(self, aime_id: int, login_bonus_data: Dict) -> Optional[int]:
|
||||
@@ -396,15 +502,18 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_login_bonus: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_login_bonuses(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(login_bonus).where(login_bonus.c.user == aime_id)
|
||||
result = self.execute(sql)
|
||||
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_mission_point(self, aime_id: int, mission_point_data: Dict) -> Optional[int]:
|
||||
def put_mission_point(
|
||||
self, aime_id: int, mission_point_data: Dict
|
||||
) -> Optional[int]:
|
||||
mission_point_data["user"] = aime_id
|
||||
|
||||
sql = insert(mission_point).values(**mission_point_data)
|
||||
@@ -415,14 +524,15 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_mission_point: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_mission_points(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(mission_point).where(mission_point.c.user == aime_id)
|
||||
result = self.execute(sql)
|
||||
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def put_event_point(self, aime_id: int, event_point_data: Dict) -> Optional[int]:
|
||||
event_point_data["user"] = aime_id
|
||||
|
||||
@@ -434,12 +544,13 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_event_point: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_event_points(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(event_point).where(event_point.c.user == aime_id)
|
||||
result = self.execute(sql)
|
||||
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_scenerio(self, aime_id: int, scenerio_data: Dict) -> Optional[int]:
|
||||
@@ -453,12 +564,13 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_scenerio: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_scenerios(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(scenerio).where(scenerio.c.user == aime_id)
|
||||
result = self.execute(sql)
|
||||
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_trade_item(self, aime_id: int, trade_item_data: Dict) -> Optional[int]:
|
||||
@@ -472,14 +584,15 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_trade_item: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_trade_items(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(trade_item).where(trade_item.c.user == aime_id)
|
||||
result = self.execute(sql)
|
||||
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def put_event_music(self, aime_id: int, event_music_data: Dict) -> Optional[int]:
|
||||
event_music_data["user"] = aime_id
|
||||
|
||||
@@ -491,12 +604,13 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_event_music: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_event_music(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(event_music).where(event_music.c.user == aime_id)
|
||||
result = self.execute(sql)
|
||||
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_tech_event(self, aime_id: int, tech_event_data: Dict) -> Optional[int]:
|
||||
@@ -510,17 +624,95 @@ class OngekiItemData(BaseData):
|
||||
self.logger.warn(f"put_tech_event: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_tech_event(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(tech_event).where(tech_event.c.user == aime_id)
|
||||
result = self.execute(sql)
|
||||
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_bosses(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(boss).where(boss.c.user == aime_id)
|
||||
result = self.execute(sql)
|
||||
|
||||
if result is None: return None
|
||||
return result.fetchall()
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_memorychapter(
|
||||
self, aime_id: int, memorychapter_data: Dict
|
||||
) -> Optional[int]:
|
||||
memorychapter_data["user"] = aime_id
|
||||
|
||||
sql = insert(memorychapter).values(**memorychapter_data)
|
||||
conflict = sql.on_duplicate_key_update(**memorychapter_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_memorychapter: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_memorychapters(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(memorychapter).where(memorychapter.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_user_gacha(self, aime_id: int, gacha_id: int) -> Optional[Row]:
|
||||
sql = gacha.select(and_(gacha.c.user == aime_id, gacha.c.gachaId == gacha_id))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_user_gachas(self, aime_id: int) -> Optional[List[Row]]:
|
||||
sql = gacha.select(gacha.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_user_gacha_supplies(self, aime_id: int) -> Optional[List[Row]]:
|
||||
sql = gacha_supply.select(gacha_supply.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_user_gacha(self, aime_id: int, gacha_id: int, **data) -> Optional[int]:
|
||||
sql = insert(gacha).values(user=aime_id, gachaId=gacha_id, **data)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(user=aime_id, gachaId=gacha_id, **data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_user_gacha: Failed to insert! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def put_user_print_detail(
|
||||
self, aime_id: int, serial_id: str, user_print_data: Dict
|
||||
) -> Optional[int]:
|
||||
sql = insert(print_detail).values(
|
||||
user=aime_id, serialId=serial_id, **user_print_data
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
user=aime_id, serialId=serial_id, **user_print_data
|
||||
)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(
|
||||
f"put_user_print_detail: Failed to insert! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
@@ -15,11 +15,13 @@ gp_log = Table(
|
||||
Column("usedCredit", Integer),
|
||||
Column("placeName", String(255)),
|
||||
Column("trxnDate", String(255)),
|
||||
Column("placeId", Integer), # Making this an FK would mess with people playing with default KC
|
||||
Column("kind", Integer),
|
||||
Column("pattern", Integer),
|
||||
Column(
|
||||
"placeId", Integer
|
||||
), # Making this an FK would mess with people playing with default KC
|
||||
Column("kind", Integer),
|
||||
Column("pattern", Integer),
|
||||
Column("currentGP", Integer),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
session_log = Table(
|
||||
@@ -32,12 +34,22 @@ session_log = Table(
|
||||
Column("playDate", String(10)),
|
||||
Column("userPlayDate", String(25)),
|
||||
Column("isPaid", Boolean),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class OngekiLogData(BaseData):
|
||||
def put_gp_log(self, aime_id: Optional[int], used_credit: int, place_name: str, tx_date: str, place_id: int,
|
||||
kind: int, pattern: int, current_gp: int) -> Optional[int]:
|
||||
def put_gp_log(
|
||||
self,
|
||||
aime_id: Optional[int],
|
||||
used_credit: int,
|
||||
place_name: str,
|
||||
tx_date: str,
|
||||
place_id: int,
|
||||
kind: int,
|
||||
pattern: int,
|
||||
current_gp: int,
|
||||
) -> Optional[int]:
|
||||
sql = insert(gp_log).values(
|
||||
user=aime_id,
|
||||
usedCredit=used_credit,
|
||||
@@ -51,5 +63,7 @@ class OngekiLogData(BaseData):
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_gp_log: Failed to insert GP log! aime_id: {aime_id} kind {kind} pattern {pattern} current_gp {current_gp}")
|
||||
return result.lastrowid
|
||||
self.logger.warn(
|
||||
f"put_gp_log: Failed to insert GP log! aime_id: {aime_id} kind {kind} pattern {pattern} current_gp {current_gp}"
|
||||
)
|
||||
return result.lastrowid
|
||||
|
||||
@@ -16,7 +16,11 @@ profile = Table(
|
||||
"ongeki_profile_data",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("userName", String(8)),
|
||||
Column("level", Integer),
|
||||
@@ -78,8 +82,10 @@ profile = Table(
|
||||
Column("overDamageBattlePoint", Integer, server_default="0"),
|
||||
Column("bestBattlePoint", Integer, server_default="0"),
|
||||
Column("lastEmoneyBrand", Integer, server_default="0"),
|
||||
Column("lastEmoneyCredit", Integer, server_default="0"),
|
||||
Column("isDialogWatchedSuggestMemory", Boolean, server_default="0"),
|
||||
UniqueConstraint("user", "version", name="ongeki_profile_profile_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
# No point setting defaults since the game sends everything on profile creation anyway
|
||||
@@ -87,7 +93,11 @@ option = Table(
|
||||
"ongeki_profile_option",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("optionSet", Integer),
|
||||
Column("speed", Integer),
|
||||
Column("mirror", Integer),
|
||||
@@ -126,14 +136,18 @@ option = Table(
|
||||
Column("stealthField", Integer),
|
||||
Column("colorWallBright", Integer),
|
||||
UniqueConstraint("user", name="ongeki_profile_option_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
activity = Table(
|
||||
"ongeki_profile_activity",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("kind", Integer),
|
||||
Column("activityId", Integer),
|
||||
Column("sortNumber", Integer),
|
||||
@@ -142,69 +156,81 @@ activity = Table(
|
||||
Column("param3", Integer),
|
||||
Column("param4", Integer),
|
||||
UniqueConstraint("user", "kind", "activityId", name="ongeki_profile_activity_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
recent_rating = Table(
|
||||
"ongeki_profile_recent_rating",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("recentRating", JSON),
|
||||
UniqueConstraint("user", name="ongeki_profile_recent_rating_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
rating_log = Table(
|
||||
"ongeki_profile_rating_log",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("highestRating", Integer),
|
||||
Column("dataVersion", String(10)),
|
||||
UniqueConstraint("user", "dataVersion", name="ongeki_profile_rating_log_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
region = Table(
|
||||
"ongeki_profile_region",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("regionId", Integer),
|
||||
Column("playCount", Integer),
|
||||
Column("created", String(25)),
|
||||
UniqueConstraint("user", "regionId", name="ongeki_profile_region_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
training_room = Table (
|
||||
training_room = Table(
|
||||
"ongeki_profile_training_room",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("roomId", Integer),
|
||||
Column("roomId", Integer),
|
||||
Column("authKey", Integer),
|
||||
Column("cardId", Integer),
|
||||
Column("valueDate", String(25)),
|
||||
UniqueConstraint("user", "roomId", name="ongeki_profile_training_room_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
kop = Table (
|
||||
kop = Table(
|
||||
"ongeki_profile_kop",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("authKey", Integer),
|
||||
Column("kopId", Integer),
|
||||
Column("kopId", Integer),
|
||||
Column("areaId", Integer),
|
||||
Column("totalTechScore", Integer),
|
||||
Column("totalPlatinumScore", Integer),
|
||||
Column("techRecordDate", String(25)),
|
||||
Column("isTotalTechNewRecord", Boolean),
|
||||
UniqueConstraint("user", "kopId", name="ongeki_profile_kop_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
rival = Table(
|
||||
@@ -212,87 +238,114 @@ rival = Table(
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("rivalUserId", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column(
|
||||
"rivalUserId",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
),
|
||||
UniqueConstraint("user", "rivalUserId", name="ongeki_profile_rival_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class OngekiProfileData(BaseData):
|
||||
def __init__(self, cfg: CoreConfig, conn: Connection) -> None:
|
||||
super().__init__(cfg, conn)
|
||||
self.date_time_format_ext = "%Y-%m-%d %H:%M:%S.%f" # needs to be lopped off at [:-5]
|
||||
self.date_time_format_ext = (
|
||||
"%Y-%m-%d %H:%M:%S.%f" # needs to be lopped off at [:-5]
|
||||
)
|
||||
self.date_time_format_short = "%Y-%m-%d"
|
||||
|
||||
def get_profile_name(self, aime_id: int, version: int) -> Optional[str]:
|
||||
sql = select(profile.c.userName).where(and_(profile.c.user == aime_id, profile.c.version == version))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
|
||||
row = result.fetchone()
|
||||
if row is None: return None
|
||||
|
||||
return row["userName"]
|
||||
|
||||
def get_profile_preview(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select([profile, option]).join(option, profile.c.user == option.c.user).filter(
|
||||
sql = select(profile.c.userName).where(
|
||||
and_(profile.c.user == aime_id, profile.c.version == version)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
|
||||
row = result.fetchone()
|
||||
if row is None:
|
||||
return None
|
||||
|
||||
return row["userName"]
|
||||
|
||||
def get_profile_preview(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = (
|
||||
select([profile, option])
|
||||
.join(option, profile.c.user == option.c.user)
|
||||
.filter(and_(profile.c.user == aime_id, profile.c.version == version))
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_profile_data(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(profile).where(and_(
|
||||
profile.c.user == aime_id,
|
||||
profile.c.version == version,
|
||||
))
|
||||
sql = select(profile).where(
|
||||
and_(
|
||||
profile.c.user == aime_id,
|
||||
profile.c.version == version,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_profile_options(self, aime_id: int) -> Optional[Row]:
|
||||
sql = select(option).where(and_(
|
||||
option.c.user == aime_id,
|
||||
))
|
||||
sql = select(option).where(
|
||||
and_(
|
||||
option.c.user == aime_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_profile_recent_rating(self, aime_id: int) -> Optional[List[Row]]:
|
||||
sql = select(recent_rating).where(recent_rating.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_profile_rating_log(self, aime_id: int) -> Optional[List[Row]]:
|
||||
sql = select(rating_log).where(recent_rating.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_profile_activity(self, aime_id: int, kind: int = None) -> Optional[List[Row]]:
|
||||
sql = select(activity).where(and_(
|
||||
activity.c.user == aime_id,
|
||||
(activity.c.kind == kind) if kind is not None else True
|
||||
))
|
||||
def get_profile_activity(
|
||||
self, aime_id: int, kind: int = None
|
||||
) -> Optional[List[Row]]:
|
||||
sql = select(activity).where(
|
||||
and_(
|
||||
activity.c.user == aime_id,
|
||||
(activity.c.kind == kind) if kind is not None else True,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def get_kop(self, aime_id: int) -> Optional[List[Row]]:
|
||||
sql = select(kop).where(kop.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
||||
def get_rivals(self, aime_id: int) -> Optional[List[Row]]:
|
||||
sql = select(rival.c.rivalUserId).where(rival.c.user == aime_id)
|
||||
|
||||
@@ -323,48 +376,62 @@ class OngekiProfileData(BaseData):
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_options: Failed to update! aime_id: {aime_id}")
|
||||
self.logger.warn(
|
||||
f"put_profile_options: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def put_profile_recent_rating(self, aime_id: int, recent_rating_data: List[Dict]) -> Optional[int]:
|
||||
def put_profile_recent_rating(
|
||||
self, aime_id: int, recent_rating_data: List[Dict]
|
||||
) -> Optional[int]:
|
||||
sql = insert(recent_rating).values(
|
||||
user=aime_id,
|
||||
recentRating=recent_rating_data
|
||||
user=aime_id, recentRating=recent_rating_data
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
recentRating=recent_rating_data
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(recentRating=recent_rating_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_recent_rating: failed to update recent rating! aime_id {aime_id}")
|
||||
self.logger.warn(
|
||||
f"put_profile_recent_rating: failed to update recent rating! aime_id {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def put_profile_bp_list(self, aime_id: int, bp_base_list: List[Dict]) -> Optional[int]:
|
||||
def put_profile_bp_list(
|
||||
self, aime_id: int, bp_base_list: List[Dict]
|
||||
) -> Optional[int]:
|
||||
pass
|
||||
|
||||
def put_profile_rating_log(self, aime_id: int, data_version: str, highest_rating: int) -> Optional[int]:
|
||||
|
||||
def put_profile_rating_log(
|
||||
self, aime_id: int, data_version: str, highest_rating: int
|
||||
) -> Optional[int]:
|
||||
sql = insert(rating_log).values(
|
||||
user=aime_id,
|
||||
dataVersion=data_version,
|
||||
highestRating=highest_rating
|
||||
user=aime_id, dataVersion=data_version, highestRating=highest_rating
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
highestRating=highest_rating
|
||||
)
|
||||
conflict = sql.on_duplicate_key_update(highestRating=highest_rating)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_rating_log: failed to update rating log! aime_id {aime_id} data_version {data_version} highest_rating {highest_rating}")
|
||||
self.logger.warn(
|
||||
f"put_profile_rating_log: failed to update rating log! aime_id {aime_id} data_version {data_version} highest_rating {highest_rating}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def put_profile_activity(self, aime_id: int, kind: int, activity_id: int, sort_num: int,
|
||||
p1: int, p2: int, p3: int, p4: int) -> Optional[int]:
|
||||
def put_profile_activity(
|
||||
self,
|
||||
aime_id: int,
|
||||
kind: int,
|
||||
activity_id: int,
|
||||
sort_num: int,
|
||||
p1: int,
|
||||
p2: int,
|
||||
p3: int,
|
||||
p4: int,
|
||||
) -> Optional[int]:
|
||||
sql = insert(activity).values(
|
||||
user=aime_id,
|
||||
kind=kind,
|
||||
@@ -373,29 +440,24 @@ class OngekiProfileData(BaseData):
|
||||
param1=p1,
|
||||
param2=p2,
|
||||
param3=p3,
|
||||
param4=p4
|
||||
param4=p4,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
sortNumber=sort_num,
|
||||
param1=p1,
|
||||
param2=p2,
|
||||
param3=p3,
|
||||
param4=p4
|
||||
sortNumber=sort_num, param1=p1, param2=p2, param3=p3, param4=p4
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_activity: failed to put activity! aime_id {aime_id} kind {kind} activity_id {activity_id}")
|
||||
self.logger.warn(
|
||||
f"put_profile_activity: failed to put activity! aime_id {aime_id} kind {kind} activity_id {activity_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def put_profile_region(self, aime_id: int, region: int, date: str) -> Optional[int]:
|
||||
sql = insert(activity).values(
|
||||
user=aime_id,
|
||||
region=region,
|
||||
playCount=1,
|
||||
created=date
|
||||
user=aime_id, region=region, playCount=1, created=date
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
@@ -404,10 +466,12 @@ class OngekiProfileData(BaseData):
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_region: failed to update! aime_id {aime_id} region {region}")
|
||||
self.logger.warn(
|
||||
f"put_profile_region: failed to update! aime_id {aime_id} region {region}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def put_training_room(self, aime_id: int, room_detail: Dict) -> Optional[int]:
|
||||
room_detail["user"] = aime_id
|
||||
|
||||
@@ -419,7 +483,7 @@ class OngekiProfileData(BaseData):
|
||||
self.logger.warn(f"put_best_score: Failed to add score! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def put_kop(self, aime_id: int, kop_data: Dict) -> Optional[int]:
|
||||
kop_data["user"] = aime_id
|
||||
|
||||
@@ -431,17 +495,16 @@ class OngekiProfileData(BaseData):
|
||||
self.logger.warn(f"put_kop: Failed to add score! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def put_rival(self, aime_id: int, rival_id: int) -> Optional[int]:
|
||||
sql = insert(rival).values(
|
||||
user = aime_id,
|
||||
rivalUserId = rival_id
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(rival = rival_id)
|
||||
def put_rival(self, aime_id: int, rival_id: int) -> Optional[int]:
|
||||
sql = insert(rival).values(user=aime_id, rivalUserId=rival_id)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(rival=rival_id)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_rival: failed to update! aime_id: {aime_id}, rival_id: {rival_id}")
|
||||
self.logger.warn(
|
||||
f"put_rival: failed to update! aime_id: {aime_id}, rival_id: {rival_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
@@ -11,14 +11,18 @@ score_best = Table(
|
||||
"ongeki_score_best",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("musicId", Integer, nullable=False),
|
||||
Column("level", Integer, nullable=False),
|
||||
Column("playCount", Integer, nullable=False),
|
||||
Column("playCount", Integer, nullable=False),
|
||||
Column("techScoreMax", Integer, nullable=False),
|
||||
Column("techScoreRank", Integer, nullable=False),
|
||||
Column("battleScoreMax", Integer, nullable=False),
|
||||
Column("battleScoreRank", Integer, nullable=False),
|
||||
Column("battleScoreRank", Integer, nullable=False),
|
||||
Column("maxComboCount", Integer, nullable=False),
|
||||
Column("maxOverKill", Float, nullable=False),
|
||||
Column("maxTeamOverKill", Float, nullable=False),
|
||||
@@ -28,15 +32,20 @@ score_best = Table(
|
||||
Column("isLock", Boolean, nullable=False),
|
||||
Column("clearStatus", Boolean, nullable=False),
|
||||
Column("isStoryWatched", Boolean, nullable=False),
|
||||
Column("platinumScoreMax", Integer),
|
||||
UniqueConstraint("user", "musicId", "level", name="ongeki_best_score_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
playlog = Table(
|
||||
"ongeki_score_playlog",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("sortNumber", Integer),
|
||||
Column("placeId", Integer),
|
||||
Column("placeName", String(255)),
|
||||
@@ -96,25 +105,32 @@ playlog = Table(
|
||||
Column("isAllBreak", Boolean),
|
||||
Column("playerRating", Integer),
|
||||
Column("battlePoint", Integer),
|
||||
mysql_charset='utf8mb4'
|
||||
Column("platinumScore", Integer),
|
||||
Column("platinumScoreMax", Integer),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
tech_count = Table(
|
||||
"ongeki_score_tech_count",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("levelId", Integer, nullable=False),
|
||||
Column("allBreakCount", Integer),
|
||||
Column("allBreakPlusCount", Integer),
|
||||
UniqueConstraint("user", "levelId", name="ongeki_tech_count_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class OngekiScoreData(BaseData):
|
||||
def get_tech_count(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
return []
|
||||
|
||||
|
||||
def put_tech_count(self, aime_id: int, tech_count_data: Dict) -> Optional[int]:
|
||||
tech_count_data["user"] = aime_id
|
||||
|
||||
@@ -126,17 +142,20 @@ class OngekiScoreData(BaseData):
|
||||
self.logger.warn(f"put_tech_count: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
|
||||
def get_best_scores(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(score_best).where(score_best.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_best_score(self, aime_id: int, song_id: int, chart_id: int = None) -> Optional[List[Dict]]:
|
||||
def get_best_score(
|
||||
self, aime_id: int, song_id: int, chart_id: int = None
|
||||
) -> Optional[List[Dict]]:
|
||||
return []
|
||||
|
||||
|
||||
def put_best_score(self, aime_id: int, music_detail: Dict) -> Optional[int]:
|
||||
music_detail["user"] = aime_id
|
||||
|
||||
@@ -158,4 +177,4 @@ class OngekiScoreData(BaseData):
|
||||
if result is None:
|
||||
self.logger.warn(f"put_playlog: Failed to add playlog! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
return result.lastrowid
|
||||
|
||||
@@ -18,7 +18,7 @@ events = Table(
|
||||
Column("name", String(255)),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
UniqueConstraint("version", "eventId", "type", name="ongeki_static_events_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
@@ -34,20 +34,210 @@ music = Table(
|
||||
Column("genre", String(255)),
|
||||
Column("level", Float),
|
||||
UniqueConstraint("version", "songId", "chartId", name="ongeki_static_music_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
gachas = Table(
|
||||
"ongeki_static_gachas",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("gachaId", Integer, nullable=False),
|
||||
Column("gachaName", String(255), nullable=False),
|
||||
Column("type", Integer, nullable=False, server_default="0"),
|
||||
Column("kind", Integer, nullable=False, server_default="0"),
|
||||
Column("isCeiling", Boolean, server_default="0"),
|
||||
Column("maxSelectPoint", Integer, server_default="0"),
|
||||
Column("ceilingCnt", Integer, server_default="10"),
|
||||
Column("changeRateCnt1", Integer, server_default="0"),
|
||||
Column("changeRateCnt2", Integer, server_default="0"),
|
||||
Column("startDate", TIMESTAMP, server_default="2018-01-01 00:00:00.0"),
|
||||
Column("endDate", TIMESTAMP, server_default="2038-01-01 00:00:00.0"),
|
||||
Column("noticeStartDate", TIMESTAMP, server_default="2018-01-01 00:00:00.0"),
|
||||
Column("noticeEndDate", TIMESTAMP, server_default="2038-01-01 00:00:00.0"),
|
||||
Column("convertEndDate", TIMESTAMP, server_default="2038-01-01 00:00:00.0"),
|
||||
UniqueConstraint("version", "gachaId", "gachaName", name="ongeki_static_gachas_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
gacha_cards = Table(
|
||||
"ongeki_static_gacha_cards",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("gachaId", Integer, nullable=False),
|
||||
Column("cardId", Integer, nullable=False),
|
||||
Column("rarity", Integer, nullable=False),
|
||||
Column("weight", Integer, server_default="1"),
|
||||
Column("isPickup", Boolean, server_default="0"),
|
||||
Column("isSelect", Boolean, server_default="0"),
|
||||
UniqueConstraint("gachaId", "cardId", name="ongeki_static_gacha_cards_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
cards = Table(
|
||||
"ongeki_static_cards",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("cardId", Integer, nullable=False),
|
||||
Column("name", String(255), nullable=False),
|
||||
Column("charaId", Integer, nullable=False),
|
||||
Column("nickName", String(255)),
|
||||
Column("school", String(255), nullable=False),
|
||||
Column("attribute", String(5), nullable=False),
|
||||
Column("gakunen", String(255), nullable=False),
|
||||
Column("rarity", Integer, nullable=False),
|
||||
Column("levelParam", String(255), nullable=False),
|
||||
Column("skillId", Integer, nullable=False),
|
||||
Column("choKaikaSkillId", Integer, nullable=False),
|
||||
Column("cardNumber", String(255)),
|
||||
UniqueConstraint("version", "cardId", name="ongeki_static_cards_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class OngekiStaticData(BaseData):
|
||||
def put_event(self, version: int, event_id: int, event_type: int, event_name: str) -> Optional[int]:
|
||||
sql = insert(events).values(
|
||||
version = version,
|
||||
eventId = event_id,
|
||||
type = event_type,
|
||||
name = event_name,
|
||||
def put_card(self, version: int, card_id: int, **card_data) -> Optional[int]:
|
||||
sql = insert(cards).values(version=version, cardId=card_id, **card_data)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(**card_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"Failed to insert card! card_id {card_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_card(self, version: int, card_id: int) -> Optional[Dict]:
|
||||
sql = cards.select(and_(cards.c.version <= version, cards.c.cardId == card_id))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_card_by_card_number(self, version: int, card_number: str) -> Optional[Dict]:
|
||||
if not card_number.startswith("[O.N.G.E.K.I.]"):
|
||||
card_number = f"[O.N.G.E.K.I.]{card_number}"
|
||||
|
||||
sql = cards.select(
|
||||
and_(cards.c.version <= version, cards.c.cardNumber == card_number)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_card_by_name(self, version: int, name: str) -> Optional[Dict]:
|
||||
sql = cards.select(and_(cards.c.version <= version, cards.c.name == name))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_cards(self, version: int) -> Optional[List[Dict]]:
|
||||
sql = cards.select(cards.c.version <= version)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_cards_by_rarity(self, version: int, rarity: int) -> Optional[List[Dict]]:
|
||||
sql = cards.select(and_(cards.c.version <= version, cards.c.rarity == rarity))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_gacha(
|
||||
self,
|
||||
version: int,
|
||||
gacha_id: int,
|
||||
gacha_name: int,
|
||||
gacha_kind: int,
|
||||
**gacha_data,
|
||||
) -> Optional[int]:
|
||||
sql = insert(gachas).values(
|
||||
version=version,
|
||||
gachaId=gacha_id,
|
||||
gachaName=gacha_name,
|
||||
kind=gacha_kind,
|
||||
**gacha_data,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
name = event_name,
|
||||
version=version,
|
||||
gachaId=gacha_id,
|
||||
gachaName=gacha_name,
|
||||
kind=gacha_kind,
|
||||
**gacha_data,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"Failed to insert gacha! gacha_id {gacha_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_gacha(self, version: int, gacha_id: int) -> Optional[Dict]:
|
||||
sql = gachas.select(
|
||||
and_(gachas.c.version <= version, gachas.c.gachaId == gacha_id)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_gachas(self, version: int) -> Optional[List[Dict]]:
|
||||
sql = gachas.select(gachas.c.version == version).order_by(
|
||||
gachas.c.gachaId.asc()
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_gacha_card(
|
||||
self, gacha_id: int, card_id: int, **gacha_card
|
||||
) -> Optional[int]:
|
||||
sql = insert(gacha_cards).values(gachaId=gacha_id, cardId=card_id, **gacha_card)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
gachaId=gacha_id, cardId=card_id, **gacha_card
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"Failed to insert gacha card! gacha_id {gacha_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_gacha_cards(self, gacha_id: int) -> Optional[List[Dict]]:
|
||||
sql = gacha_cards.select(gacha_cards.c.gachaId == gacha_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_event(
|
||||
self, version: int, event_id: int, event_type: int, event_name: str
|
||||
) -> Optional[int]:
|
||||
sql = insert(events).values(
|
||||
version=version,
|
||||
eventId=event_id,
|
||||
type=event_type,
|
||||
name=event_name,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
name=event_name,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
@@ -57,63 +247,88 @@ class OngekiStaticData(BaseData):
|
||||
return result.lastrowid
|
||||
|
||||
def get_event(self, version: int, event_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(events).where(and_(events.c.version == version, events.c.eventId == event_id))
|
||||
|
||||
sql = select(events).where(
|
||||
and_(events.c.version == version, events.c.eventId == event_id)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_events(self, version: int) -> Optional[List[Dict]]:
|
||||
sql = select(events).where(events.c.version == version)
|
||||
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_enabled_events(self, version: int) -> Optional[List[Dict]]:
|
||||
sql = select(events).where(and_(events.c.version == version, events.c.enabled == True))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_chart(self, version: int, song_id: int, chart_id: int, title: str, artist: str, genre: str, level: float) -> Optional[int]:
|
||||
def get_enabled_events(self, version: int) -> Optional[List[Dict]]:
|
||||
sql = select(events).where(
|
||||
and_(events.c.version == version, events.c.enabled == True)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_chart(
|
||||
self,
|
||||
version: int,
|
||||
song_id: int,
|
||||
chart_id: int,
|
||||
title: str,
|
||||
artist: str,
|
||||
genre: str,
|
||||
level: float,
|
||||
) -> Optional[int]:
|
||||
sql = insert(music).values(
|
||||
version = version,
|
||||
songId = song_id,
|
||||
chartId = chart_id,
|
||||
title = title,
|
||||
artist = artist,
|
||||
genre = genre,
|
||||
level = level,
|
||||
version=version,
|
||||
songId=song_id,
|
||||
chartId=chart_id,
|
||||
title=title,
|
||||
artist=artist,
|
||||
genre=genre,
|
||||
level=level,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
title = title,
|
||||
artist = artist,
|
||||
genre = genre,
|
||||
level = level,
|
||||
title=title,
|
||||
artist=artist,
|
||||
genre=genre,
|
||||
level=level,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"Failed to insert chart! song_id: {song_id}, chart_id: {chart_id}")
|
||||
self.logger.warn(
|
||||
f"Failed to insert chart! song_id: {song_id}, chart_id: {chart_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_chart(self, version: int, song_id: int, chart_id: int = None) -> Optional[List[Dict]]:
|
||||
def get_chart(
|
||||
self, version: int, song_id: int, chart_id: int = None
|
||||
) -> Optional[List[Dict]]:
|
||||
pass
|
||||
|
||||
def get_music(self, version: int) -> Optional[List[Dict]]:
|
||||
pass
|
||||
|
||||
def get_music_chart(self, version: int, song_id: int, chart_id: int) -> Optional[List[Row]]:
|
||||
sql = select(music).where(and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
music.c.chartId == chart_id
|
||||
))
|
||||
def get_music_chart(
|
||||
self, version: int, song_id: int, chart_id: int
|
||||
) -> Optional[List[Row]]:
|
||||
sql = select(music).where(
|
||||
and_(
|
||||
music.c.version == version,
|
||||
music.c.songId == song_id,
|
||||
music.c.chartId == chart_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
@@ -5,11 +5,12 @@ from titles.ongeki.base import OngekiBase
|
||||
from titles.ongeki.const import OngekiConstants
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
|
||||
|
||||
class OngekiSummer(OngekiBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_SUMMER
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.10.00"
|
||||
|
||||
@@ -5,11 +5,12 @@ from titles.ongeki.base import OngekiBase
|
||||
from titles.ongeki.const import OngekiConstants
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
|
||||
|
||||
class OngekiSummerPlus(OngekiBase):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_SUMMER_PLUS
|
||||
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.15.00"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user