forked from Hay1tsme/artemis
add back games, conform them to new title dispatch
This commit is contained in:
18
titles/ongeki/__init__.py
Normal file
18
titles/ongeki/__init__.py
Normal file
@ -0,0 +1,18 @@
|
||||
from titles.ongeki.index import OngekiServlet
|
||||
from titles.ongeki.const import OngekiConstants
|
||||
from titles.ongeki.database import OngekiData
|
||||
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
|
934
titles/ongeki/base.py
Normal file
934
titles/ongeki/base.py
Normal file
@ -0,0 +1,934 @@
|
||||
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.ongeki.const import OngekiConstants
|
||||
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
|
||||
USUALLY = 2
|
||||
GOOD = 3
|
||||
GREAT = 4
|
||||
EXCELLENT = 5
|
||||
UNBELIEVABLE_GOLD = 6
|
||||
UNBELIEVABLE_RAINBOW = 7
|
||||
|
||||
class OngekiBattlePointGrade(Enum):
|
||||
FRESHMAN = 0
|
||||
KYU10 = 1
|
||||
KYU9 = 2
|
||||
KYU8 = 3
|
||||
KYU7 = 4
|
||||
KYU6 = 5
|
||||
KYU5 = 6
|
||||
KYU4 = 7
|
||||
KYU3 = 8
|
||||
KYU2 = 9
|
||||
KYU1 = 10
|
||||
DAN1 = 11
|
||||
DAN2 = 12
|
||||
DAN3 = 13
|
||||
DAN4 = 14
|
||||
DAN5 = 15
|
||||
DAN6 = 16
|
||||
DAN7 = 17
|
||||
DAN8 = 18
|
||||
DAN9 = 19
|
||||
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
|
||||
SSSp = 11
|
||||
|
||||
class OngekiDifficulty(Enum):
|
||||
BASIC = 0
|
||||
ADVANCED = 1
|
||||
EXPERT = 2
|
||||
MASTER = 3
|
||||
LUNATIC = 10
|
||||
|
||||
class OngekiGPLogKind(Enum):
|
||||
NONE = 0
|
||||
BUY1_START = 1
|
||||
BUY2_START = 2
|
||||
BUY3_START = 3
|
||||
BUY1_ADD = 4
|
||||
BUY2_ADD = 5
|
||||
BUY3_ADD = 6
|
||||
FIRST_PLAY = 7
|
||||
COMPENSATION = 8
|
||||
|
||||
PAY_PLAY = 11
|
||||
PAY_TIME = 12
|
||||
PAY_MAS_UNLOCK = 13
|
||||
PAY_MONEY = 14
|
||||
|
||||
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_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)
|
||||
return {
|
||||
"gameSetting": {
|
||||
"dataVersion": "1.00.00",
|
||||
"onlineDataVersion": "1.00.00",
|
||||
"isMaintenance": "false",
|
||||
"requestInterval": 10,
|
||||
"rebootStartTime": reboot_start,
|
||||
"rebootEndTime": reboot_end,
|
||||
"isBackgroundDistribute": "false",
|
||||
"maxCountCharacter": 50,
|
||||
"maxCountCard": 300,
|
||||
"maxCountItem": 300,
|
||||
"maxCountMusic": 50,
|
||||
"maxCountMusicItem": 300,
|
||||
"macCountRivalMusic": 300,
|
||||
},
|
||||
"isDumpUpload": "false",
|
||||
"isAou": "true",
|
||||
}
|
||||
|
||||
def handle_get_game_idlist_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
Gets lists of song IDs, either disabled songs or recomended songs depending on type?
|
||||
"""
|
||||
# type - int
|
||||
# id - int
|
||||
return {"type": data["type"], "length": 0, "gameIdlistList": []}
|
||||
|
||||
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"}
|
||||
]}
|
||||
|
||||
def handle_game_login_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "gameLogin"}
|
||||
|
||||
def handle_game_logout_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "gameLogout"}
|
||||
|
||||
def handle_extend_lock_time_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "ExtendLockTimeApi"}
|
||||
|
||||
def handle_get_game_reward_api_request(self, data: Dict) -> Dict:
|
||||
# TODO: reward list
|
||||
return {"length": 0, "gameRewardList": []}
|
||||
|
||||
def handle_get_game_present_api_request(self, data: Dict) -> Dict:
|
||||
return {"length": 0, "gamePresentList": []}
|
||||
|
||||
def handle_get_game_message_api_request(self, data: Dict) -> Dict:
|
||||
return {"length": 0, "gameMessageList": []}
|
||||
|
||||
def handle_get_game_sale_api_request(self, data: Dict) -> Dict:
|
||||
return {"length": 0, "gameSaleList": []}
|
||||
|
||||
def handle_get_game_tech_music_api_request(self, data: Dict) -> Dict:
|
||||
return {"length": 0, "gameTechMusicList": []}
|
||||
|
||||
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
||||
|
||||
def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
|
||||
|
||||
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "upsertClientBookkeeping"}
|
||||
|
||||
def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "upsertClientDevelop"}
|
||||
|
||||
def handle_upsert_client_error_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "upsertClientError"}
|
||||
|
||||
def handle_upsert_user_gplog_api_request(self, data: Dict) -> Dict:
|
||||
user = data["userId"]
|
||||
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"])
|
||||
|
||||
return {"returnCode": 1, "apiName": "UpsertUserGplogApi"}
|
||||
|
||||
def handle_extend_lock_time_api_request(self, data: Dict) -> Dict:
|
||||
return {"returnCode": 1, "apiName": "ExtendLockTimeApi"}
|
||||
|
||||
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}
|
||||
|
||||
def handle_get_game_id_list_api_request(self, data: Dict) -> Dict:
|
||||
game_idlist: list[str, Any] = [] #1 to 230 & 8000 to 8050
|
||||
|
||||
if data["type"] == 1:
|
||||
for i in range(1,231):
|
||||
game_idlist.append({"type": 1, "id": i})
|
||||
return {"type": data["type"], "length": len(game_idlist), "gameIdlistList": game_idlist}
|
||||
elif data["type"] == 2:
|
||||
for i in range(8000,8051):
|
||||
game_idlist.append({"type": 2, "id": i})
|
||||
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:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"isLogin": False,
|
||||
"lastLoginDate": "0000-00-00 00:00:00",
|
||||
"userName": "",
|
||||
"reincarnationNum": 0,
|
||||
"level": 0,
|
||||
"exp": 0,
|
||||
"playerRating": 0,
|
||||
"lastGameId": "",
|
||||
"lastRomVersion": "",
|
||||
"lastDataVersion": "",
|
||||
"lastPlayDate": "",
|
||||
"nameplateId": 0,
|
||||
"trophyId": 0,
|
||||
"cardId": 0,
|
||||
"dispPlayerLv": 0,
|
||||
"dispRating": 0,
|
||||
"dispBP": 0,
|
||||
"headphone": 0,
|
||||
"banStatus": 0,
|
||||
"isWarningConfirmed": True,
|
||||
}
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"isLogin": False,
|
||||
"lastLoginDate": profile["lastPlayDate"],
|
||||
"userName": profile["userName"],
|
||||
"reincarnationNum": profile["reincarnationNum"],
|
||||
"level": profile["level"],
|
||||
"exp": profile["exp"],
|
||||
"playerRating": profile["playerRating"],
|
||||
"lastGameId": profile["lastGameId"],
|
||||
"lastRomVersion": profile["lastRomVersion"],
|
||||
"lastDataVersion": profile["lastDataVersion"],
|
||||
"lastPlayDate": profile["lastPlayDate"],
|
||||
"nameplateId": profile["nameplateId"],
|
||||
"trophyId": profile["trophyId"],
|
||||
"cardId": profile["cardId"],
|
||||
"dispPlayerLv": profile["dispPlayerLv"],
|
||||
"dispRating": profile["dispRating"],
|
||||
"dispBP": profile["dispBP"],
|
||||
"headphone": profile["headphone"],
|
||||
"banStatus": profile["banStatus"],
|
||||
"isWarningConfirmed": True,
|
||||
}
|
||||
|
||||
def handle_get_user_tech_count_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
Gets the number of AB and ABPs a player has per-difficulty (7, 7+, 8, etc)
|
||||
The game sends this in upsert so we don't have to calculate it all out thankfully
|
||||
"""
|
||||
utcl = self.data.score.get_tech_count(data["userId"])
|
||||
userTechCountList = []
|
||||
|
||||
for tc in utcl:
|
||||
tc.pop("id")
|
||||
tc.pop("user")
|
||||
userTechCountList.append(tc)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(userTechCountList),
|
||||
"userTechCountList": userTechCountList,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
tech_evt = []
|
||||
for evt in user_tech_event_list:
|
||||
tmp = evt._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
tech_evt.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(tech_evt),
|
||||
"userTechEventList": tech_evt,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
evt_ranking = []
|
||||
#for evt in user_event_ranking_list:
|
||||
# tmp = evt._asdict()
|
||||
# tmp.pop("id")
|
||||
# tmp.pop("user")
|
||||
# evt_ranking.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(evt_ranking),
|
||||
"userTechEventRankingList": evt_ranking,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
for kop in kop_list:
|
||||
kop.pop("user")
|
||||
kop.pop("id")
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(kop_list),
|
||||
"userKopList": kop_list,
|
||||
}
|
||||
|
||||
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
|
||||
song_list = self.util_generate_music_list(data["userId"])
|
||||
max_ct = data["maxCount"]
|
||||
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
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(song_list[start_idx:end_idx]),
|
||||
"nextIndex": next_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": []}
|
||||
|
||||
items: list[Dict[str, Any]] = []
|
||||
for i in range(data["nextIndex"] % 10000000000, len(p)):
|
||||
if len(items) > data["maxCount"]:
|
||||
break
|
||||
tmp = p[i]._asdict()
|
||||
tmp.pop("user")
|
||||
tmp.pop("id")
|
||||
items.append(tmp)
|
||||
|
||||
xout = kind * 10000000000 + (data["nextIndex"] % 10000000000) + len(items)
|
||||
|
||||
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}
|
||||
|
||||
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 {}
|
||||
|
||||
# get the dict representation of the row so we can modify values
|
||||
user_opts = o._asdict()
|
||||
|
||||
# remove the values the game doesn't want
|
||||
user_opts.pop("id")
|
||||
user_opts.pop("user")
|
||||
|
||||
return {"userId": data["userId"], "userOption": user_opts}
|
||||
|
||||
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 {}
|
||||
|
||||
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"]
|
||||
|
||||
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 {}
|
||||
|
||||
evt_ranking = []
|
||||
#for evt in user_event_ranking_list:
|
||||
# tmp = evt._asdict()
|
||||
# tmp.pop("id")
|
||||
# tmp.pop("user")
|
||||
# evt_ranking.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(evt_ranking),
|
||||
"userEventRankingList": evt_ranking,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
login_bonuses = []
|
||||
for scenerio in user_login_bonus_list:
|
||||
tmp = scenerio._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
login_bonuses.append(tmp)
|
||||
|
||||
return {
|
||||
"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 {}
|
||||
profile = json.loads(p["data"])
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(profile["userBpBaseList"]),
|
||||
"userBpBaseList": profile["userBpBaseList"],
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
userRecentRatingList = recent_rating["recentRating"]
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(userRecentRatingList),
|
||||
"userRecentRatingList": userRecentRatingList,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
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"],
|
||||
})
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(user_activity),
|
||||
"kind": data["kind"],
|
||||
"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 {}
|
||||
|
||||
story_list = []
|
||||
for story in user_stories:
|
||||
tmp = story._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
story_list.append(tmp)
|
||||
|
||||
return {
|
||||
"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 {}
|
||||
|
||||
chapter_list = []
|
||||
for chapter in user_chapters:
|
||||
tmp = chapter._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
chapter_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(chapter_list),
|
||||
"userChapterList": chapter_list
|
||||
}
|
||||
|
||||
def handle_get_user_training_room_by_key_api_request(self, data: Dict) -> Dict:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": 0,
|
||||
"userTrainingRoomList": [],
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
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),
|
||||
"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 {}
|
||||
|
||||
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),
|
||||
"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 {}
|
||||
|
||||
deck_list = []
|
||||
for deck in decks:
|
||||
tmp = deck._asdict()
|
||||
tmp.pop("user")
|
||||
tmp.pop("id")
|
||||
deck_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(deck_list),
|
||||
"userDeckList": deck_list,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
trade_item_list = []
|
||||
for trade_item in user_trade_items:
|
||||
tmp = trade_item._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
trade_item_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(trade_item_list),
|
||||
"userTradeItemList": trade_item_list,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
scenerio_list = []
|
||||
for scenerio in user_scenerio:
|
||||
tmp = scenerio._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
scenerio_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(scenerio_list),
|
||||
"userScenarioList": scenerio_list,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
userRatinglogList = []
|
||||
for rating in rating_log:
|
||||
tmp = rating._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
userRatinglogList.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(userRatinglogList),
|
||||
"userRatinglogList": userRatinglogList,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
mission_point_list = []
|
||||
for evt_music in user_mission_point_list:
|
||||
tmp = evt_music._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
mission_point_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(mission_point_list),
|
||||
"userMissionPointList": mission_point_list,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
event_point_list = []
|
||||
for evt_music in user_event_point_list:
|
||||
tmp = evt_music._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
event_point_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(event_point_list),
|
||||
"userEventPointList": event_point_list,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
music_item_list = []
|
||||
for evt_music in user_music_item_list:
|
||||
tmp = evt_music._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
music_item_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(music_item_list),
|
||||
"userMusicItemList": music_item_list,
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
evt_music_list = []
|
||||
for evt_music in user_evt_music_list:
|
||||
tmp = evt_music._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
evt_music_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(evt_music_list),
|
||||
"userEventMusicList": evt_music_list,
|
||||
}
|
||||
|
||||
def handle_get_user_boss_api_request(self, data: Dict) -> Dict:
|
||||
p = self.data.item.get_bosses(data["userId"])
|
||||
if p is None: return {}
|
||||
|
||||
boss_list = []
|
||||
for boss in p:
|
||||
tmp = boss._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
boss_list.append(tmp)
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(boss_list),
|
||||
"userBossList": boss_list,
|
||||
}
|
||||
|
||||
def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
||||
upsert = data["upsertUserAll"]
|
||||
user_id = data["userId"]
|
||||
|
||||
# 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])
|
||||
|
||||
if "userOption" in upsert and len(upsert["userOption"]) > 0:
|
||||
self.data.profile.put_profile_options(user_id, upsert["userOption"][0])
|
||||
|
||||
if "userPlaylogList" in upsert:
|
||||
for playlog in upsert["userPlaylogList"]:
|
||||
self.data.score.put_playlog(user_id, playlog)
|
||||
|
||||
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 "userRecentRatingList" in upsert:
|
||||
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)
|
||||
|
||||
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 "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"])
|
||||
|
||||
if "userBossList" in upsert:
|
||||
for x in upsert["userBossList"]:
|
||||
self.data.item.put_boss(user_id, x)
|
||||
|
||||
if "userTechCountList" in upsert:
|
||||
for x in upsert["userTechCountList"]:
|
||||
self.data.score.put_tech_count(user_id, x)
|
||||
|
||||
if "userScenerioList" in upsert:
|
||||
for x in upsert["userScenerioList"]:
|
||||
self.data.item.put_scenerio(user_id, x)
|
||||
|
||||
if "userTradeItemList" in upsert:
|
||||
for x in upsert["userTradeItemList"]:
|
||||
self.data.item.put_trade_item(user_id, x)
|
||||
|
||||
if "userEventMusicList" in upsert:
|
||||
for x in upsert["userEventMusicList"]:
|
||||
self.data.item.put_event_music(user_id, x)
|
||||
|
||||
if "userTechEventList" in upsert:
|
||||
for x in upsert["userTechEventList"]:
|
||||
self.data.item.put_tech_event(user_id, x)
|
||||
|
||||
if "userKopList" in upsert:
|
||||
for x in upsert["userKopList"]:
|
||||
self.data.profile.put_kop(user_id, x)
|
||||
|
||||
return {'returnCode': 1, 'apiName': 'upsertUserAll'}
|
||||
|
||||
def handle_get_user_rival_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
Added in Bright
|
||||
"""
|
||||
rival_list = self.data.profile.get_rivals(data["userId"])
|
||||
if rival_list is None or len(rival_list) < 1:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"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:
|
||||
"""
|
||||
Added in Bright
|
||||
"""
|
||||
rivals = []
|
||||
|
||||
for rival in data["userRivalList"]:
|
||||
name = self.data.profile.get_profile_name(rival["rivalUserId"], self.version)
|
||||
if name is None:
|
||||
continue
|
||||
|
||||
rivals.append({
|
||||
"rivalUserId": rival["rival"],
|
||||
"rivalUserName": name
|
||||
})
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"length": len(rivals),
|
||||
"userRivalDataList": rivals,
|
||||
}
|
||||
|
||||
def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
|
||||
"""
|
||||
Added in Bright
|
||||
"""
|
||||
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
|
||||
})
|
||||
|
||||
for song in music["userMusicList"]:
|
||||
song["userRivalMusicDetailList"] = song["userMusicDetailList"]
|
||||
song.pop("userMusicDetailList")
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"rivalUserId": rival_id,
|
||||
"length": music["length"],
|
||||
"nextIndex": music["nextIndex"],
|
||||
"userRivalMusicList": music["userMusicList"],
|
||||
}
|
||||
|
||||
@cached(2)
|
||||
def util_generate_music_list(self, user_id: int) -> List:
|
||||
music_detail = self.data.score.get_best_scores(user_id)
|
||||
song_list = []
|
||||
|
||||
for md in music_detail:
|
||||
found = False
|
||||
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]
|
||||
})
|
||||
|
||||
return song_list
|
21
titles/ongeki/bright.py
Normal file
21
titles/ongeki/bright.py
Normal file
@ -0,0 +1,21 @@
|
||||
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.const import OngekiConstants
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
|
||||
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
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.30.00"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.30.00"
|
||||
return ret
|
17
titles/ongeki/config.py
Normal file
17
titles/ongeki/config.py
Normal file
@ -0,0 +1,17 @@
|
||||
from core.config import CoreConfig
|
||||
|
||||
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)
|
||||
|
||||
@property
|
||||
def loglevel(self) -> int:
|
||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'ongeki', 'server', 'loglevel', default="info"))
|
||||
|
||||
class OngekiConfig(dict):
|
||||
def __init__(self) -> None:
|
||||
self.server = OngekiServerConfig(self)
|
50
titles/ongeki/const.py
Normal file
50
titles/ongeki/const.py
Normal file
@ -0,0 +1,50 @@
|
||||
from typing import Final, Dict
|
||||
from enum import Enum
|
||||
class OngekiConstants():
|
||||
GAME_CODE = "SDDT"
|
||||
|
||||
VER_ONGEKI = 0
|
||||
VER_ONGEKI_PLUS = 1
|
||||
VER_ONGEKI_SUMMER = 2
|
||||
VER_ONGEKI_SUMMER_PLUS = 3
|
||||
VER_ONGEKI_RED = 4
|
||||
VER_ONGEKI_RED_PLUS = 5
|
||||
VER_ONGEKI_BRIGHT = 6
|
||||
|
||||
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',
|
||||
])
|
||||
|
||||
# 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):
|
||||
Basic = 0
|
||||
Advanced = 1
|
||||
Expert = 2
|
||||
Master = 3
|
||||
Lunatic = 10
|
||||
|
||||
VERSION_NAMES = ("ONGEKI", "ONGEKI+", "ONGEKI Summer", "ONGEKI Summer+", "ONGEKI Red", "ONGEKI Red+",
|
||||
"ONGEKI Bright")
|
||||
|
||||
@classmethod
|
||||
def game_ver_to_string(cls, ver: int):
|
||||
return cls.VERSION_NAMES[ver]
|
14
titles/ongeki/database.py
Normal file
14
titles/ongeki/database.py
Normal file
@ -0,0 +1,14 @@
|
||||
from core.data import Data
|
||||
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)
|
||||
|
||||
self.item = OngekiItemData(cfg, self.session)
|
||||
self.profile = OngekiProfileData(cfg, self.session)
|
||||
self.score = OngekiScoreData(cfg, self.session)
|
||||
self.static = OngekiStaticData(cfg, self.session)
|
||||
self.log = OngekiLogData(cfg, self.session)
|
114
titles/ongeki/index.py
Normal file
114
titles/ongeki/index.py
Normal file
@ -0,0 +1,114 @@
|
||||
from twisted.web.http import Request
|
||||
import json
|
||||
import inflection
|
||||
import yaml
|
||||
import string
|
||||
import logging, coloredlogs
|
||||
import zlib
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.ongeki.config import OngekiConfig
|
||||
from titles.ongeki.const import OngekiConstants
|
||||
from titles.ongeki.base import OngekiBase
|
||||
from titles.ongeki.plus import OngekiPlus
|
||||
from titles.ongeki.summer import OngekiSummer
|
||||
from titles.ongeki.summerplus import OngekiSummerPlus
|
||||
from titles.ongeki.red import OngekiRed
|
||||
from titles.ongeki.redplus import OngekiRedPlus
|
||||
from titles.ongeki.bright import OngekiBright
|
||||
|
||||
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")))
|
||||
|
||||
self.versions = [
|
||||
OngekiBase(core_cfg, self.game_cfg),
|
||||
OngekiPlus(core_cfg, self.game_cfg),
|
||||
OngekiSummer(core_cfg, self.game_cfg),
|
||||
OngekiSummerPlus(core_cfg, self.game_cfg),
|
||||
OngekiRed(core_cfg, self.game_cfg),
|
||||
OngekiRedPlus(core_cfg, self.game_cfg),
|
||||
OngekiBright(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.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)
|
||||
|
||||
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]
|
||||
|
||||
if version < 105: # 1.0
|
||||
internal_ver = OngekiConstants.VER_ONGEKI
|
||||
elif version >= 105 and version < 110: # Plus
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_PLUS
|
||||
elif version >= 110 and version < 115: # Summer
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_SUMMER
|
||||
elif version >= 115 and version < 120: # Summer Plus
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_SUMMER_PLUS
|
||||
elif version >= 120 and version < 125: # Red
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_RED
|
||||
elif version >= 125 and version < 130: # Red Plus
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_RED_PLUS
|
||||
elif version >= 130 and version < 135: # Red Plus
|
||||
internal_ver = OngekiConstants.VER_ONGEKI_BRIGHT
|
||||
|
||||
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("{\"stat\": \"0\"}".encode("utf-8"))
|
||||
|
||||
req_data = json.loads(unzip)
|
||||
|
||||
self.logger.info(f"v{version} {endpoint} request - {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"))
|
||||
|
||||
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}")
|
||||
|
||||
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||
|
||||
|
||||
|
17
titles/ongeki/plus.py
Normal file
17
titles/ongeki/plus.py
Normal file
@ -0,0 +1,17 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
from core.config import CoreConfig
|
||||
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"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.05.00"
|
||||
return ret
|
89
titles/ongeki/read.py
Normal file
89
titles/ongeki/read.py
Normal file
@ -0,0 +1,89 @@
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
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.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:
|
||||
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)}")
|
||||
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")
|
||||
|
||||
def read_events(self, base_dir: str) -> None:
|
||||
self.logger.info(f"Reading events from {base_dir}...")
|
||||
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
for dir in dirs:
|
||||
if os.path.exists(f"{root}/{dir}/Event.xml"):
|
||||
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
|
||||
|
||||
|
||||
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}...")
|
||||
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
for dir in dirs:
|
||||
if os.path.exists(f"{root}/{dir}/Music.xml"):
|
||||
strdata = ""
|
||||
|
||||
with open(f"{root}/{dir}/Music.xml", "r", encoding="utf-8") as f:
|
||||
strdata = f.read()
|
||||
|
||||
troot = ET.fromstring(strdata)
|
||||
|
||||
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
|
||||
|
||||
fumens = troot.find("FumenData")
|
||||
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}")
|
||||
|
17
titles/ongeki/red.py
Normal file
17
titles/ongeki/red.py
Normal file
@ -0,0 +1,17 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
from core.config import CoreConfig
|
||||
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"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.20.00"
|
||||
return ret
|
23
titles/ongeki/redplus.py
Normal file
23
titles/ongeki/redplus.py
Normal file
@ -0,0 +1,23 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
from core.config import CoreConfig
|
||||
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"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.25.00"
|
||||
ret["gameSetting"]["maxCountCharacter"] = 50
|
||||
ret["gameSetting"]["maxCountCard"] = 300
|
||||
ret["gameSetting"]["maxCountItem"] = 300
|
||||
ret["gameSetting"]["maxCountMusic"] = 50
|
||||
ret["gameSetting"]["maxCountMusicItem"] = 300
|
||||
ret["gameSetting"]["macCountRivalMusic"] = 300
|
||||
return ret
|
7
titles/ongeki/schema/__init__.py
Normal file
7
titles/ongeki/schema/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from titles.ongeki.schema.profile import OngekiProfileData
|
||||
from titles.ongeki.schema.item import OngekiItemData
|
||||
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]
|
526
titles/ongeki/schema/item.py
Normal file
526
titles/ongeki/schema/item.py
Normal file
@ -0,0 +1,526 @@
|
||||
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.sql import func, select
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
from core.data.schema import BaseData, metadata
|
||||
|
||||
card = Table(
|
||||
"ongeki_user_card",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("cardId", Integer),
|
||||
Column("digitalStock", Integer),
|
||||
Column("analogStock", Integer),
|
||||
Column("level", Integer),
|
||||
Column("maxLevel", Integer),
|
||||
Column("exp", Integer),
|
||||
Column("printCount", Integer),
|
||||
Column("useCount", Integer),
|
||||
Column("isNew", Boolean),
|
||||
Column("kaikaDate", String(25)),
|
||||
Column("choKaikaDate", String(25)),
|
||||
Column("skillId", Integer),
|
||||
Column("isAcquired", Boolean),
|
||||
Column("created", String(25)),
|
||||
UniqueConstraint("user", "cardId", name="ongeki_user_card_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
deck = Table(
|
||||
"ongeki_user_deck",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("deckId", Integer),
|
||||
Column("cardId1", Integer),
|
||||
Column("cardId2", Integer),
|
||||
Column("cardId3", Integer),
|
||||
UniqueConstraint("user", "deckId", name="ongeki_user_deck_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
character = Table(
|
||||
"ongeki_user_character",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("characterId", Integer),
|
||||
Column("costumeId", Integer),
|
||||
Column("attachmentId", Integer),
|
||||
Column("playCount", Integer),
|
||||
Column("intimateLevel", Integer),
|
||||
Column("intimateCount", Integer),
|
||||
Column("intimateCountRewarded", Integer),
|
||||
Column("intimateCountDate", String(25)),
|
||||
Column("isNew", Boolean),
|
||||
UniqueConstraint("user", "characterId", name="ongeki_user_character_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
boss = Table (
|
||||
"ongeki_user_boss",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("musicId", Integer),
|
||||
Column("damage", Integer),
|
||||
Column("isClear", Boolean),
|
||||
Column("eventId", Integer),
|
||||
UniqueConstraint("user", "musicId", "eventId", name="ongeki_user_boss_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
story = Table (
|
||||
"ongeki_user_story",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("storyId", Integer),
|
||||
Column("jewelCount", Integer),
|
||||
Column("lastChapterId", Integer),
|
||||
Column("lastPlayMusicId", Integer),
|
||||
Column("lastPlayMusicCategory", Integer),
|
||||
Column("lastPlayMusicLevel", Integer),
|
||||
UniqueConstraint("user", "storyId", name="ongeki_user_story_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
chapter = Table(
|
||||
"ongeki_user_chapter",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("chapterId", Integer),
|
||||
Column("jewelCount", Integer),
|
||||
Column("isStoryWatched", Boolean),
|
||||
Column("isClear", Boolean),
|
||||
Column("lastPlayMusicId", Integer),
|
||||
Column("lastPlayMusicCategory", Integer),
|
||||
Column("lastPlayMusicLevel", Integer),
|
||||
Column("skipTiming1", Integer),
|
||||
Column("skipTiming2", Integer),
|
||||
UniqueConstraint("user", "chapterId", name="ongeki_user_chapter_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
item = Table(
|
||||
"ongeki_user_item",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("itemKind", Integer),
|
||||
Column("itemId", Integer),
|
||||
Column("stock", Integer),
|
||||
Column("isValid", Boolean),
|
||||
UniqueConstraint("user", "itemKind", "itemId", name="ongeki_user_item_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
music_item = Table(
|
||||
"ongeki_user_music_item",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("musicId", Integer),
|
||||
Column("status", Integer),
|
||||
UniqueConstraint("user", "musicId", name="ongeki_user_music_item_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
login_bonus = Table(
|
||||
"ongeki_user_login_bonus",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("bonusId", Integer),
|
||||
Column("bonusCount", Integer),
|
||||
Column("lastUpdateDate", String(25)),
|
||||
UniqueConstraint("user", "bonusId", name="ongeki_user_login_bonus_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
event_point = Table(
|
||||
"ongeki_user_event_point",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("eventId", Integer),
|
||||
Column("point", Integer),
|
||||
Column("isRankingRewarded", Boolean),
|
||||
UniqueConstraint("user", "eventId", name="ongeki_user_event_point_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
mission_point = Table(
|
||||
"ongeki_user_mission_point",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("eventId", Integer),
|
||||
Column("point", Integer),
|
||||
UniqueConstraint("user", "eventId", name="ongeki_user_mission_point_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
scenerio = Table(
|
||||
"ongeki_user_scenerio",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("scenarioId", Integer),
|
||||
Column("playCount", Integer),
|
||||
UniqueConstraint("user", "scenarioId", name="ongeki_user_scenerio_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
trade_item = Table(
|
||||
"ongeki_user_trade_item",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("chapterId", Integer),
|
||||
Column("tradeItemId", Integer),
|
||||
Column("tradeCount", Integer),
|
||||
UniqueConstraint("user", "chapterId", "tradeItemId", name="ongeki_user_trade_item_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
event_music = Table(
|
||||
"ongeki_user_event_music",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("eventId", Integer),
|
||||
Column("type", Integer),
|
||||
Column("musicId", Integer),
|
||||
Column("level", Integer),
|
||||
Column("techScoreMax", Integer),
|
||||
Column("platinumScoreMax", Integer),
|
||||
Column("techRecordDate", String(25)),
|
||||
Column("isTechNewRecord", Boolean),
|
||||
UniqueConstraint("user", "eventId", "type", "musicId", "level", name="ongeki_user_event_music"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
tech_event = Table(
|
||||
"ongeki_user_tech_event",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("eventId", Integer),
|
||||
Column("totalTechScore", Integer),
|
||||
Column("totalPlatinumScore", Integer),
|
||||
Column("techRecordDate", String(25)),
|
||||
Column("isRankingRewarded", Boolean),
|
||||
Column("isTotalTechNewRecord", Boolean),
|
||||
UniqueConstraint("user", "eventId", name="ongeki_user_tech_event_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
|
||||
class OngekiItemData(BaseData):
|
||||
def put_card(self, aime_id: int, card_data: Dict) -> Optional[int]:
|
||||
card_data["user"] = aime_id
|
||||
|
||||
sql = insert(card).values(**card_data)
|
||||
conflict = sql.on_duplicate_key_update(**card_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_card: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_cards(self, aime_id: int) -> Optional[List[Dict]]:
|
||||
sql = select(card).where(card.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_character(self, aime_id: int, character_data: Dict) -> Optional[int]:
|
||||
character_data["user"] = aime_id
|
||||
|
||||
sql = insert(character).values(**character_data)
|
||||
conflict = sql.on_duplicate_key_update(**character_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
def put_deck(self, aime_id: int, deck_data: Dict) -> Optional[int]:
|
||||
deck_data["user"] = aime_id
|
||||
|
||||
sql = insert(deck).values(**deck_data)
|
||||
conflict = sql.on_duplicate_key_update(**deck_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
def put_boss(self, aime_id: int, boss_data: Dict) -> Optional[int]:
|
||||
boss_data["user"] = aime_id
|
||||
|
||||
sql = insert(boss).values(**boss_data)
|
||||
conflict = sql.on_duplicate_key_update(**boss_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
|
||||
sql = insert(story).values(**story_data)
|
||||
conflict = sql.on_duplicate_key_update(**story_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
def put_chapter(self, aime_id: int, chapter_data: Dict) -> Optional[int]:
|
||||
chapter_data["user"] = aime_id
|
||||
|
||||
sql = insert(chapter).values(**chapter_data)
|
||||
conflict = sql.on_duplicate_key_update(**chapter_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
def put_item(self, aime_id: int, item_data: Dict) -> Optional[int]:
|
||||
item_data["user"] = aime_id
|
||||
|
||||
sql = insert(item).values(**item_data)
|
||||
conflict = sql.on_duplicate_key_update(**item_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
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))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_music_item(self, aime_id: int, music_item_data: Dict) -> Optional[int]:
|
||||
music_item_data["user"] = aime_id
|
||||
|
||||
sql = insert(music_item).values(**music_item_data)
|
||||
conflict = sql.on_duplicate_key_update(**music_item_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
def put_login_bonus(self, aime_id: int, login_bonus_data: Dict) -> Optional[int]:
|
||||
login_bonus_data["user"] = aime_id
|
||||
|
||||
sql = insert(login_bonus).values(**login_bonus_data)
|
||||
conflict = sql.on_duplicate_key_update(**login_bonus_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
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)
|
||||
conflict = sql.on_duplicate_key_update(**mission_point_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
def put_event_point(self, aime_id: int, event_point_data: Dict) -> Optional[int]:
|
||||
event_point_data["user"] = aime_id
|
||||
|
||||
sql = insert(event_point).values(**event_point_data)
|
||||
conflict = sql.on_duplicate_key_update(**event_point_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
def put_scenerio(self, aime_id: int, scenerio_data: Dict) -> Optional[int]:
|
||||
scenerio_data["user"] = aime_id
|
||||
|
||||
sql = insert(scenerio).values(**scenerio_data)
|
||||
conflict = sql.on_duplicate_key_update(**scenerio_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
def put_trade_item(self, aime_id: int, trade_item_data: Dict) -> Optional[int]:
|
||||
trade_item_data["user"] = aime_id
|
||||
|
||||
sql = insert(trade_item).values(**trade_item_data)
|
||||
conflict = sql.on_duplicate_key_update(**trade_item_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
def put_event_music(self, aime_id: int, event_music_data: Dict) -> Optional[int]:
|
||||
event_music_data["user"] = aime_id
|
||||
|
||||
sql = insert(event_music).values(**event_music_data)
|
||||
conflict = sql.on_duplicate_key_update(**event_music_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
def put_tech_event(self, aime_id: int, tech_event_data: Dict) -> Optional[int]:
|
||||
tech_event_data["user"] = aime_id
|
||||
|
||||
sql = insert(tech_event).values(**tech_event_data)
|
||||
conflict = sql.on_duplicate_key_update(**tech_event_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
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()
|
55
titles/ongeki/schema/log.py
Normal file
55
titles/ongeki/schema/log.py
Normal file
@ -0,0 +1,55 @@
|
||||
from typing import Dict, Optional
|
||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
from core.data.schema import BaseData, metadata
|
||||
|
||||
gp_log = Table(
|
||||
"ongeki_gp_log",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
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("currentGP", Integer),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
session_log = Table(
|
||||
"ongeki_session_log",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("sortNumber", Integer),
|
||||
Column("placeId", Integer),
|
||||
Column("playDate", String(10)),
|
||||
Column("userPlayDate", String(25)),
|
||||
Column("isPaid", Boolean),
|
||||
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]:
|
||||
sql = insert(gp_log).values(
|
||||
user=aime_id,
|
||||
usedCredit=used_credit,
|
||||
placeName=place_name,
|
||||
trxnDate=tx_date,
|
||||
placeId=place_id,
|
||||
kind=kind,
|
||||
pattern=pattern,
|
||||
currentGP=current_gp,
|
||||
)
|
||||
|
||||
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
|
447
titles/ongeki/schema/profile.py
Normal file
447
titles/ongeki/schema/profile.py
Normal file
@ -0,0 +1,447 @@
|
||||
from typing import Dict, List, Optional
|
||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger
|
||||
from sqlalchemy.engine.base import Connection
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
from core.data.schema import BaseData, metadata
|
||||
from core.config import CoreConfig
|
||||
|
||||
# Cammel case column names technically don't follow the other games but
|
||||
# it makes it way easier on me to not fuck with what the games has
|
||||
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("version", Integer, nullable=False),
|
||||
Column("userName", String(8)),
|
||||
Column("level", Integer),
|
||||
Column("reincarnationNum", Integer),
|
||||
Column("exp", Integer),
|
||||
Column("point", Integer),
|
||||
Column("totalPoint", Integer),
|
||||
Column("playCount", Integer),
|
||||
Column("jewelCount", Integer),
|
||||
Column("totalJewelCount", Integer),
|
||||
Column("medalCount", Integer),
|
||||
Column("playerRating", Integer),
|
||||
Column("highestRating", Integer),
|
||||
Column("battlePoint", Integer),
|
||||
Column("nameplateId", Integer),
|
||||
Column("trophyId", Integer),
|
||||
Column("cardId", Integer),
|
||||
Column("characterId", Integer),
|
||||
Column("characterVoiceNo", Integer),
|
||||
Column("tabSetting", Integer),
|
||||
Column("tabSortSetting", Integer),
|
||||
Column("cardCategorySetting", Integer),
|
||||
Column("cardSortSetting", Integer),
|
||||
Column("playedTutorialBit", Integer),
|
||||
Column("firstTutorialCancelNum", Integer),
|
||||
Column("sumTechHighScore", BigInteger),
|
||||
Column("sumTechBasicHighScore", BigInteger),
|
||||
Column("sumTechAdvancedHighScore", BigInteger),
|
||||
Column("sumTechExpertHighScore", BigInteger),
|
||||
Column("sumTechMasterHighScore", BigInteger),
|
||||
Column("sumTechLunaticHighScore", BigInteger),
|
||||
Column("sumBattleHighScore", BigInteger),
|
||||
Column("sumBattleBasicHighScore", BigInteger),
|
||||
Column("sumBattleAdvancedHighScore", BigInteger),
|
||||
Column("sumBattleExpertHighScore", BigInteger),
|
||||
Column("sumBattleMasterHighScore", BigInteger),
|
||||
Column("sumBattleLunaticHighScore", BigInteger),
|
||||
Column("eventWatchedDate", String(255)),
|
||||
Column("cmEventWatchedDate", String(255)),
|
||||
Column("firstGameId", String(8)),
|
||||
Column("firstRomVersion", String(8)),
|
||||
Column("firstDataVersion", String(8)),
|
||||
Column("firstPlayDate", String(255)),
|
||||
Column("lastGameId", String(8)),
|
||||
Column("lastRomVersion", String(8)),
|
||||
Column("lastDataVersion", String(8)),
|
||||
Column("compatibleCmVersion", String(8)),
|
||||
Column("lastPlayDate", String(255)),
|
||||
Column("lastPlaceId", Integer),
|
||||
Column("lastPlaceName", String(255)),
|
||||
Column("lastRegionId", Integer),
|
||||
Column("lastRegionName", String(255)),
|
||||
Column("lastAllNetId", Integer),
|
||||
Column("lastClientId", String(16)),
|
||||
Column("lastUsedDeckId", Integer),
|
||||
Column("lastPlayMusicLevel", Integer),
|
||||
Column("banStatus", Integer, server_default="0"),
|
||||
Column("rivalScoreCategorySetting", Integer, server_default="0"),
|
||||
Column("overDamageBattlePoint", Integer, server_default="0"),
|
||||
Column("bestBattlePoint", Integer, server_default="0"),
|
||||
Column("lastEmoneyBrand", Integer, server_default="0"),
|
||||
UniqueConstraint("user", "version", name="ongeki_profile_profile_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
# No point setting defaults since the game sends everything on profile creation anyway
|
||||
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("optionSet", Integer),
|
||||
Column("speed", Integer),
|
||||
Column("mirror", Integer),
|
||||
Column("judgeTiming", Integer),
|
||||
Column("judgeAdjustment", Integer),
|
||||
Column("abort", Integer),
|
||||
Column("tapSound", Integer),
|
||||
Column("volGuide", Integer),
|
||||
Column("volAll", Integer),
|
||||
Column("volTap", Integer),
|
||||
Column("volCrTap", Integer),
|
||||
Column("volHold", Integer),
|
||||
Column("volSide", Integer),
|
||||
Column("volFlick", Integer),
|
||||
Column("volBell", Integer),
|
||||
Column("volEnemy", Integer),
|
||||
Column("volSkill", Integer),
|
||||
Column("volDamage", Integer),
|
||||
Column("colorField", Integer),
|
||||
Column("colorLaneBright", Integer),
|
||||
Column("colorLane", Integer),
|
||||
Column("colorSide", Integer),
|
||||
Column("effectDamage", Integer),
|
||||
Column("effectPos", Integer),
|
||||
Column("judgeDisp", Integer),
|
||||
Column("judgePos", Integer),
|
||||
Column("judgeBreak", Integer),
|
||||
Column("judgeHit", Integer),
|
||||
Column("platinumBreakDisp", Integer),
|
||||
Column("judgeCriticalBreak", Integer),
|
||||
Column("matching", Integer),
|
||||
Column("dispPlayerLv", Integer),
|
||||
Column("dispRating", Integer),
|
||||
Column("dispBP", Integer),
|
||||
Column("headphone", Integer),
|
||||
Column("stealthField", Integer),
|
||||
Column("colorWallBright", Integer),
|
||||
UniqueConstraint("user", name="ongeki_profile_option_uk"),
|
||||
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("kind", Integer),
|
||||
Column("activityId", Integer),
|
||||
Column("sortNumber", Integer),
|
||||
Column("param1", Integer),
|
||||
Column("param2", Integer),
|
||||
Column("param3", Integer),
|
||||
Column("param4", Integer),
|
||||
UniqueConstraint("user", "kind", "activityId", name="ongeki_profile_activity_uk"),
|
||||
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("recentRating", JSON),
|
||||
UniqueConstraint("user", name="ongeki_profile_recent_rating_uk"),
|
||||
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("highestRating", Integer),
|
||||
Column("dataVersion", String(10)),
|
||||
UniqueConstraint("user", "dataVersion", name="ongeki_profile_rating_log_uk"),
|
||||
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("regionId", Integer),
|
||||
Column("playCount", Integer),
|
||||
Column("created", String(25)),
|
||||
UniqueConstraint("user", "regionId", name="ongeki_profile_region_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
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("authKey", Integer),
|
||||
Column("cardId", Integer),
|
||||
Column("valueDate", String(25)),
|
||||
UniqueConstraint("user", "roomId", name="ongeki_profile_training_room_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
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("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'
|
||||
)
|
||||
|
||||
rival = Table(
|
||||
"ongeki_profile_rival",
|
||||
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")),
|
||||
UniqueConstraint("user", "rivalUserId", name="ongeki_profile_rival_uk"),
|
||||
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_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(
|
||||
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,
|
||||
))
|
||||
|
||||
result = self.execute(sql)
|
||||
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,
|
||||
))
|
||||
|
||||
result = self.execute(sql)
|
||||
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
|
||||
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
|
||||
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
|
||||
))
|
||||
|
||||
result = self.execute(sql)
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
def get_rivals(self, aime_id: int) -> Optional[List[Row]]:
|
||||
sql = select(rival.c.rivalUserId).where(rival.c.user == aime_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_profile_data(self, aime_id: int, version: int, data: Dict) -> Optional[int]:
|
||||
data["user"] = aime_id
|
||||
data["version"] = version
|
||||
data.pop("accessCode")
|
||||
|
||||
sql = insert(profile).values(**data)
|
||||
conflict = sql.on_duplicate_key_update(**data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_profile_data: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def put_profile_options(self, aime_id: int, options_data: Dict) -> Optional[int]:
|
||||
options_data["user"] = aime_id
|
||||
|
||||
sql = insert(option).values(**options_data)
|
||||
conflict = sql.on_duplicate_key_update(**options_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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]:
|
||||
sql = insert(recent_rating).values(
|
||||
user=aime_id,
|
||||
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}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
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]:
|
||||
sql = insert(rating_log).values(
|
||||
user=aime_id,
|
||||
dataVersion=data_version,
|
||||
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}")
|
||||
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]:
|
||||
sql = insert(activity).values(
|
||||
user=aime_id,
|
||||
kind=kind,
|
||||
activityId=activity_id,
|
||||
sortNumber=sort_num,
|
||||
param1=p1,
|
||||
param2=p2,
|
||||
param3=p3,
|
||||
param4=p4
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
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}")
|
||||
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
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
playCount=activity.c.playCount + 1,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
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
|
||||
|
||||
sql = insert(training_room).values(**room_detail)
|
||||
conflict = sql.on_duplicate_key_update(**room_detail)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
|
||||
sql = insert(kop).values(**kop_data)
|
||||
conflict = sql.on_duplicate_key_update(**kop_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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)
|
||||
|
||||
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}")
|
||||
return None
|
||||
return result.lastrowid
|
161
titles/ongeki/schema/score.py
Normal file
161
titles/ongeki/schema/score.py
Normal file
@ -0,0 +1,161 @@
|
||||
from typing import Dict, List, Optional
|
||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, Float
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
from core.data.schema import BaseData, metadata
|
||||
|
||||
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("musicId", Integer, nullable=False),
|
||||
Column("level", 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("maxComboCount", Integer, nullable=False),
|
||||
Column("maxOverKill", Float, nullable=False),
|
||||
Column("maxTeamOverKill", Float, nullable=False),
|
||||
Column("isFullBell", Boolean, nullable=False),
|
||||
Column("isFullCombo", Boolean, nullable=False),
|
||||
Column("isAllBreake", Boolean, nullable=False),
|
||||
Column("isLock", Boolean, nullable=False),
|
||||
Column("clearStatus", Boolean, nullable=False),
|
||||
Column("isStoryWatched", Boolean, nullable=False),
|
||||
UniqueConstraint("user", "musicId", "level", name="ongeki_best_score_uk"),
|
||||
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("sortNumber", Integer),
|
||||
Column("placeId", Integer),
|
||||
Column("placeName", String(255)),
|
||||
Column("playDate", TIMESTAMP),
|
||||
Column("userPlayDate", TIMESTAMP),
|
||||
Column("musicId", Integer),
|
||||
Column("level", Integer),
|
||||
Column("playKind", Integer),
|
||||
Column("eventId", Integer),
|
||||
Column("eventName", String(255)),
|
||||
Column("eventPoint", Integer),
|
||||
Column("playedUserId1", Integer),
|
||||
Column("playedUserId2", Integer),
|
||||
Column("playedUserId3", Integer),
|
||||
Column("playedUserName1", String(8)),
|
||||
Column("playedUserName2", String(8)),
|
||||
Column("playedUserName3", String(8)),
|
||||
Column("playedMusicLevel1", Integer),
|
||||
Column("playedMusicLevel2", Integer),
|
||||
Column("playedMusicLevel3", Integer),
|
||||
Column("cardId1", Integer),
|
||||
Column("cardId2", Integer),
|
||||
Column("cardId3", Integer),
|
||||
Column("cardLevel1", Integer),
|
||||
Column("cardLevel2", Integer),
|
||||
Column("cardLevel3", Integer),
|
||||
Column("cardAttack1", Integer),
|
||||
Column("cardAttack2", Integer),
|
||||
Column("cardAttack3", Integer),
|
||||
Column("bossCharaId", Integer),
|
||||
Column("bossLevel", Integer),
|
||||
Column("bossAttribute", Integer),
|
||||
Column("clearStatus", Integer),
|
||||
Column("techScore", Integer),
|
||||
Column("techScoreRank", Integer),
|
||||
Column("battleScore", Integer),
|
||||
Column("battleScoreRank", Integer),
|
||||
Column("maxCombo", Integer),
|
||||
Column("judgeMiss", Integer),
|
||||
Column("judgeHit", Integer),
|
||||
Column("judgeBreak", Integer),
|
||||
Column("judgeCriticalBreak", Integer),
|
||||
Column("rateTap", Integer),
|
||||
Column("rateHold", Integer),
|
||||
Column("rateFlick", Integer),
|
||||
Column("rateSideTap", Integer),
|
||||
Column("rateSideHold", Integer),
|
||||
Column("bellCount", Integer),
|
||||
Column("totalBellCount", Integer),
|
||||
Column("damageCount", Integer),
|
||||
Column("overDamage", Integer),
|
||||
Column("isTechNewRecord", Boolean),
|
||||
Column("isBattleNewRecord", Boolean),
|
||||
Column("isOverDamageNewRecord", Boolean),
|
||||
Column("isFullCombo", Boolean),
|
||||
Column("isFullBell", Boolean),
|
||||
Column("isAllBreak", Boolean),
|
||||
Column("playerRating", Integer),
|
||||
Column("battlePoint", 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("levelId", Integer, nullable=False),
|
||||
Column("allBreakCount", Integer),
|
||||
Column("allBreakPlusCount", Integer),
|
||||
UniqueConstraint("user", "levelId", name="ongeki_tech_count_uk"),
|
||||
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
|
||||
|
||||
sql = insert(tech_count).values(**tech_count_data)
|
||||
conflict = sql.on_duplicate_key_update(**tech_count_data)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
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
|
||||
return result.fetchall()
|
||||
|
||||
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
|
||||
|
||||
sql = insert(score_best).values(**music_detail)
|
||||
conflict = sql.on_duplicate_key_update(**music_detail)
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_best_score: Failed to add score! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def put_playlog(self, aime_id: int, playlog_data: Dict) -> Optional[int]:
|
||||
playlog_data["user"] = aime_id
|
||||
|
||||
sql = insert(playlog).values(**playlog_data)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
self.logger.warn(f"put_playlog: Failed to add playlog! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
119
titles/ongeki/schema/static.py
Normal file
119
titles/ongeki/schema/static.py
Normal file
@ -0,0 +1,119 @@
|
||||
from typing import Dict, List, Optional
|
||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, Float
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
from core.data.schema import BaseData, metadata
|
||||
|
||||
events = Table(
|
||||
"ongeki_static_events",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("version", Integer),
|
||||
Column("eventId", Integer),
|
||||
Column("type", Integer),
|
||||
Column("name", String(255)),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
UniqueConstraint("version", "eventId", "type", name="ongeki_static_events_uk"),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
|
||||
|
||||
music = Table(
|
||||
"ongeki_static_music",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("version", Integer),
|
||||
Column("songId", Integer),
|
||||
Column("chartId", Integer),
|
||||
Column("title", String(255)),
|
||||
Column("artist", String(255)),
|
||||
Column("genre", String(255)),
|
||||
Column("level", Float),
|
||||
UniqueConstraint("version", "songId", "chartId", name="ongeki_static_music_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,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
name = event_name,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warn(f"Failed to insert event! event_id {event_id}")
|
||||
return None
|
||||
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))
|
||||
|
||||
result = self.execute(sql)
|
||||
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
|
||||
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,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
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}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
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
|
||||
))
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None: return None
|
||||
return result.fetchone()
|
17
titles/ongeki/summer.py
Normal file
17
titles/ongeki/summer.py
Normal file
@ -0,0 +1,17 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
from core.config import CoreConfig
|
||||
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"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.10.00"
|
||||
return ret
|
17
titles/ongeki/summerplus.py
Normal file
17
titles/ongeki/summerplus.py
Normal file
@ -0,0 +1,17 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
from core.config import CoreConfig
|
||||
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"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.15.00"
|
||||
return ret
|
Reference in New Issue
Block a user