add back games, conform them to new title dispatch

This commit is contained in:
Hay1tsme
2023-02-17 01:02:21 -05:00
parent f18e939dd0
commit 7e3396a7ff
214 changed files with 19412 additions and 23 deletions

18
titles/ongeki/__init__.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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]

View 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()

View 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

View 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

View 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

View 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
View 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

View 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