begin move

This commit is contained in:
Kevin Trocolli
2024-01-09 03:07:04 -05:00
parent b056ff218d
commit 14fa0f5e8e
82 changed files with 1683 additions and 1712 deletions

View File

@@ -11,7 +11,7 @@ class ChuniAir(ChuniBase):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_AIR
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.10.00"
return ret

View File

@@ -11,7 +11,7 @@ class ChuniAirPlus(ChuniBase):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_AIR_PLUS
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.15.00"
return ret

View File

@@ -13,7 +13,7 @@ class ChuniAmazon(ChuniBase):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_AMAZON
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.30.00"
return ret

View File

@@ -13,7 +13,7 @@ class ChuniAmazonPlus(ChuniBase):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_AMAZON_PLUS
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.35.00"
return ret

View File

@@ -22,7 +22,7 @@ class ChuniBase:
self.game = ChuniConstants.GAME_CODE
self.version = ChuniConstants.VER_CHUNITHM
def handle_game_login_api_request(self, data: Dict) -> Dict:
async def handle_game_login_api_request(self, data: Dict) -> Dict:
"""
Handles the login bonus logic, required for the game because
getUserLoginBonus gets called after getUserItem and therefore the
@@ -119,11 +119,11 @@ class ChuniBase:
return {"returnCode": 1}
def handle_game_logout_api_request(self, data: Dict) -> Dict:
async def handle_game_logout_api_request(self, data: Dict) -> Dict:
# self.data.base.log_event("chuni", "logout", logging.INFO, {"version": self.version, "user": data["userId"]})
return {"returnCode": 1}
def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
async def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
game_charge_list = self.data.static.get_enabled_charges(self.version)
if game_charge_list is None or len(game_charge_list) == 0:
@@ -145,7 +145,7 @@ class ChuniBase:
)
return {"length": len(charges), "gameChargeList": charges}
def handle_get_game_event_api_request(self, data: Dict) -> Dict:
async def handle_get_game_event_api_request(self, data: Dict) -> Dict:
game_events = self.data.static.get_enabled_events(self.version)
if game_events is None or len(game_events) == 0:
@@ -177,10 +177,10 @@ class ChuniBase:
"gameEventList": event_list,
}
def handle_get_game_idlist_api_request(self, data: Dict) -> Dict:
async def handle_get_game_idlist_api_request(self, data: Dict) -> Dict:
return {"type": data["type"], "length": 0, "gameIdlistList": []}
def handle_get_game_message_api_request(self, data: Dict) -> Dict:
async def handle_get_game_message_api_request(self, data: Dict) -> Dict:
return {
"type": data["type"],
"length": 1,
@@ -193,14 +193,14 @@ class ChuniBase:
}]
}
def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
rankings = self.data.score.get_rankings(self.version)
return {"type": data["type"], "gameRankingList": rankings}
def handle_get_game_sale_api_request(self, data: Dict) -> Dict:
async def handle_get_game_sale_api_request(self, data: Dict) -> Dict:
return {"type": data["type"], "length": 0, "gameSaleList": []}
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
# if reboot start/end time is not defined use the default behavior of being a few hours ago
if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
reboot_start = datetime.strftime(
@@ -240,7 +240,7 @@ class ChuniBase:
"isDumpUpload": "false",
"isAou": "false",
}
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
async def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
user_activity_list = self.data.profile.get_profile_activity(
data["userId"], data["kind"]
)
@@ -261,7 +261,7 @@ class ChuniBase:
"userActivityList": activity_list,
}
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
async def handle_get_user_character_api_request(self, data: Dict) -> Dict:
characters = self.data.item.get_characters(data["userId"])
if characters is None:
return {
@@ -296,7 +296,7 @@ class ChuniBase:
"userCharacterList": character_list,
}
def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
async def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
user_charge_list = self.data.profile.get_profile_charge(data["userId"])
charge_list = []
@@ -312,14 +312,14 @@ class ChuniBase:
"userChargeList": charge_list,
}
def handle_get_user_recent_player_api_request(self, data: Dict) -> Dict:
async def handle_get_user_recent_player_api_request(self, data: Dict) -> Dict:
return {
"userId": data["userId"],
"length": 0,
"userRecentPlayerList": [], # playUserId, playUserName, playDate, friendPoint
}
def handle_get_user_course_api_request(self, data: Dict) -> Dict:
async def handle_get_user_course_api_request(self, data: Dict) -> Dict:
user_course_list = self.data.score.get_courses(data["userId"])
if user_course_list is None:
return {
@@ -354,7 +354,7 @@ class ChuniBase:
"userCourseList": course_list,
}
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -366,7 +366,7 @@ class ChuniBase:
return {"userId": data["userId"], "userData": profile}
def handle_get_user_data_ex_api_request(self, data: Dict) -> Dict:
async def handle_get_user_data_ex_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_data_ex(data["userId"], self.version)
if p is None:
return {}
@@ -378,7 +378,7 @@ class ChuniBase:
return {"userId": data["userId"], "userDataEx": profile}
def handle_get_user_duel_api_request(self, data: Dict) -> Dict:
async def handle_get_user_duel_api_request(self, data: Dict) -> Dict:
user_duel_list = self.data.item.get_duels(data["userId"])
if user_duel_list is None:
return {}
@@ -396,7 +396,7 @@ class ChuniBase:
"userDuelList": duel_list,
}
def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
async def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_rival(data["rivalId"])
if p is None:
return {}
@@ -409,7 +409,7 @@ class ChuniBase:
"userRivalData": userRivalData
}
def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
async def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
rival_id = data["rivalId"]
next_index = int(data["nextIndex"])
max_count = int(data["maxCount"])
@@ -462,7 +462,7 @@ class ChuniBase:
return result
def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
async def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
user_fav_item_list = []
# still needs to be implemented on WebUI
@@ -482,14 +482,14 @@ class ChuniBase:
"userFavoriteItemList": user_fav_item_list,
}
def handle_get_user_favorite_music_api_request(self, data: Dict) -> Dict:
async def handle_get_user_favorite_music_api_request(self, data: Dict) -> Dict:
"""
This is handled via the webui, which we don't have right now
"""
return {"userId": data["userId"], "length": 0, "userFavoriteMusicList": []}
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
async def handle_get_user_item_api_request(self, data: Dict) -> Dict:
kind = int(int(data["nextIndex"]) / 10000000000)
next_idx = int(int(data["nextIndex"]) % 10000000000)
user_item_list = self.data.item.get_items(data["userId"], kind)
@@ -526,7 +526,7 @@ class ChuniBase:
"userItemList": items,
}
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
user_login_bonus = self.data.item.get_all_login_bonus(user_id, self.version)
# ignore the loginBonus request if its disabled in config
@@ -552,7 +552,7 @@ class ChuniBase:
"userLoginBonusList": user_login_list,
}
def handle_get_user_map_api_request(self, data: Dict) -> Dict:
async def handle_get_user_map_api_request(self, data: Dict) -> Dict:
user_map_list = self.data.item.get_maps(data["userId"])
if user_map_list is None:
return {}
@@ -570,7 +570,7 @@ class ChuniBase:
"userMapList": map_list,
}
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
async def handle_get_user_music_api_request(self, data: Dict) -> Dict:
music_detail = self.data.score.get_scores(data["userId"])
if music_detail is None:
return {
@@ -629,7 +629,7 @@ class ChuniBase:
"userMusicList": song_list, # 240
}
def handle_get_user_option_api_request(self, data: Dict) -> Dict:
async def handle_get_user_option_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_option(data["userId"])
option = p._asdict()
@@ -638,7 +638,7 @@ class ChuniBase:
return {"userId": data["userId"], "userGameOption": option}
def handle_get_user_option_ex_api_request(self, data: Dict) -> Dict:
async def handle_get_user_option_ex_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_option_ex(data["userId"])
option = p._asdict()
@@ -650,7 +650,7 @@ class ChuniBase:
def read_wtf8(self, src):
return bytes([ord(c) for c in src]).decode("utf-8")
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
async def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_preview(data["userId"], self.version)
if profile is None:
return None
@@ -692,7 +692,7 @@ class ChuniBase:
"userNameEx": profile["userName"],
}
def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
async def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
recent_rating_list = self.data.profile.get_profile_recent_rating(data["userId"])
if recent_rating_list is None:
return {
@@ -707,7 +707,7 @@ class ChuniBase:
"userRecentRatingList": recent_rating_list["recentRating"],
}
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
async def handle_get_user_region_api_request(self, data: Dict) -> Dict:
# TODO: Region
return {
"userId": data["userId"],
@@ -715,7 +715,7 @@ class ChuniBase:
"userRegionList": [],
}
def handle_get_user_team_api_request(self, data: Dict) -> Dict:
async def handle_get_user_team_api_request(self, data: Dict) -> Dict:
# Default values
team_id = 65535
team_name = self.game_cfg.team.team_name
@@ -750,7 +750,7 @@ class ChuniBase:
},
}
def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict:
async def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict:
return {
"userId": data["userId"],
"length": 0,
@@ -758,7 +758,7 @@ class ChuniBase:
"teamCourseSettingList": [],
}
def handle_get_team_course_setting_api_request_proto(self, data: Dict) -> Dict:
async def handle_get_team_course_setting_api_request_proto(self, data: Dict) -> Dict:
return {
"userId": data["userId"],
"length": 1,
@@ -782,7 +782,7 @@ class ChuniBase:
],
}
def handle_get_team_course_rule_api_request(self, data: Dict) -> Dict:
async def handle_get_team_course_rule_api_request(self, data: Dict) -> Dict:
return {
"userId": data["userId"],
"length": 0,
@@ -790,7 +790,7 @@ class ChuniBase:
"teamCourseRuleList": []
}
def handle_get_team_course_rule_api_request_proto(self, data: Dict) -> Dict:
async def handle_get_team_course_rule_api_request_proto(self, data: Dict) -> Dict:
return {
"userId": data["userId"],
"length": 1,
@@ -807,7 +807,7 @@ class ChuniBase:
],
}
def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
upsert = data["upsertUserAll"]
user_id = data["userId"]
@@ -927,28 +927,28 @@ class ChuniBase:
return {"returnCode": "1"}
def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
# add tickets after they got bought, this makes sure the tickets are
# still valid after an unsuccessful logout
self.data.profile.put_profile_charge(data["userId"], data["userCharge"])
return {"returnCode": "1"}
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
return {"returnCode": "1"}
def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict:
return {"returnCode": "1"}
def handle_upsert_client_error_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_error_api_request(self, data: Dict) -> Dict:
return {"returnCode": "1"}
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
return {"returnCode": "1"}
def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
return {"returnCode": "1"}
def handle_get_user_net_battle_data_api_request(self, data: Dict) -> Dict:
async def handle_get_user_net_battle_data_api_request(self, data: Dict) -> Dict:
return {
"userId": data["userId"],
"userNetBattleData": {"recentNBSelectMusicList": []},

View File

@@ -13,7 +13,7 @@ class ChuniCrystal(ChuniBase):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.40.00"
return ret

View File

@@ -13,7 +13,7 @@ class ChuniCrystalPlus(ChuniBase):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.45.00"
return ret

View File

@@ -1,4 +1,6 @@
from twisted.web.http import Request
from starlette.requests import Request
from starlette.routing import Route
from starlette.responses import Response
import logging, coloredlogs
from logging.handlers import TimedRotatingFileHandler
import zlib
@@ -33,7 +35,6 @@ from .newplus import ChuniNewPlus
from .sun import ChuniSun
from .sunplus import ChuniSunPlus
class ChuniServlet(BaseServlet):
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
super().__init__(core_cfg, cfg_dir)
@@ -124,15 +125,6 @@ class ChuniServlet(BaseServlet):
f"Hashed v{version} method {method_fixed} with {bytes.fromhex(keys[2])} to get {hash.hex()}"
)
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
return (
[],
[
("render_POST", "/{game}/{version}/ChuniServlet/{endpoint}", {}),
("render_POST", "/{game}/{version}/ChuniServlet/MatchingServer/{endpoint}", {})
]
)
@classmethod
def is_game_enabled(
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
@@ -150,19 +142,25 @@ class ChuniServlet(BaseServlet):
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_code}/{game_ver}/", self.core_cfg.title.hostname)
return (f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_code}/{game_ver}/", self.core_cfg.server.hostname)
return (f"http://{self.core_cfg.title.hostname}/{game_code}/{game_ver}/", self.core_cfg.title.hostname)
return (f"http://{self.core_cfg.server.hostname}/{game_code}/{game_ver}/", self.core_cfg.server.hostname)
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
endpoint = matchers['endpoint']
version = int(matchers['version'])
game_code = matchers['game']
def get_routes(self) -> List[Route]:
return [
Route("/{game:str}/{version:int}/ChuniServlet/{endpoint:str}", self.render_POST, methods=['POST']),
Route("/{game:str}/{version:int}/ChuniServlet/MatchingServer/{endpoint:str}", self.render_POST, methods=['POST']),
]
async def render_POST(self, request: Request) -> bytes:
endpoint: str = request.path_params.get('endpoint')
version: int = request.path_params.get('version')
game_code: str = request.path_params.get('game')
if endpoint.lower() == "ping":
return zlib.compress(b'{"returnCode": "1"}')
return Response(zlib.compress(b'{"returnCode": "1"}'))
req_raw = request.content.getvalue()
req_raw = await request.body()
encrtped = False
internal_ver = 0
@@ -201,7 +199,7 @@ class ChuniServlet(BaseServlet):
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
elif game_code == "SDGS": # Int
if version < 110: # SUPERSTAR
internal_ver = ChuniConstants.PARADISE
internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE # FIXME: Not sure what was intended to go here? was just "PARADISE"
elif version >= 110 and version < 115: # NEW
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
elif version >= 115 and version < 120: # NEW PLUS!!
@@ -216,20 +214,20 @@ class ChuniServlet(BaseServlet):
# doing encrypted. The likelyhood of false positives is low but
# technically not 0
if internal_ver < ChuniConstants.VER_CHUNITHM_NEW:
endpoint = request.getHeader("User-Agent").split("#")[0]
endpoint = request.headers.get("User-Agent").split("#")[0]
else:
if internal_ver not in self.hash_table:
self.logger.error(
f"v{version} does not support encryption or no keys entered"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
elif endpoint.lower() not in self.hash_table[internal_ver]:
self.logger.error(
f"No hash found for v{version} endpoint {endpoint}"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
endpoint = self.hash_table[internal_ver][endpoint.lower()]
@@ -246,7 +244,7 @@ class ChuniServlet(BaseServlet):
self.logger.error(
f"Failed to decrypt v{version} request to {endpoint} -> {e}"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
encrtped = True
@@ -258,7 +256,7 @@ class ChuniServlet(BaseServlet):
self.logger.error(
f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
try:
unzip = zlib.decompress(req_raw)
@@ -267,7 +265,7 @@ class ChuniServlet(BaseServlet):
self.logger.error(
f"Failed to decompress v{version} {endpoint} request -> {e}"
)
return b""
return Response(zlib.compress(b'{"stat": "0"}'))
req_data = json.loads(unzip)
@@ -285,11 +283,11 @@ class ChuniServlet(BaseServlet):
else:
try:
handler = getattr(handler_cls, func_to_find)
resp = handler(req_data)
resp = await handler(req_data)
except Exception as e:
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
if resp == None:
resp = {"returnCode": 1}
@@ -299,7 +297,7 @@ class ChuniServlet(BaseServlet):
zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
if not encrtped:
return zipped
return Response(zipped)
padded = pad(zipped, 16)
@@ -309,4 +307,4 @@ class ChuniServlet(BaseServlet):
bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]),
)
return crypt.encrypt(padded)
return Response(crypt.encrypt(padded))

View File

@@ -33,7 +33,7 @@ class ChuniNew(ChuniBase):
if self.version == ChuniConstants.VER_CHUNITHM_SUN_PLUS:
return "215"
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
# use UTC time and convert it to JST time by adding +9
# matching therefore starts one hour before and lasts for 8 hours
match_start = datetime.strftime(
@@ -82,26 +82,26 @@ class ChuniNew(ChuniBase):
"matchErrorLimit": self.game_cfg.matching.match_error_limit,
"romVersion": self.game_cfg.version.version(self.version)["rom"],
"dataVersion": self.game_cfg.version.version(self.version)["data"],
"matchingUri": f"http://{self.core_cfg.title.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
"matchingUriX": f"http://{self.core_cfg.title.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
"matchingUri": f"http://{self.core_cfg.server.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
"matchingUriX": f"http://{self.core_cfg.server.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
# might be really important for online battle to connect the cabs via UDP port 50201
"udpHolePunchUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
"reflectorUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
"udpHolePunchUri": f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
"reflectorUri": f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
},
"isDumpUpload": False,
"isAou": False,
}
def handle_remove_token_api_request(self, data: Dict) -> Dict:
async def handle_remove_token_api_request(self, data: Dict) -> Dict:
return {"returnCode": "1"}
def handle_delete_token_api_request(self, data: Dict) -> Dict:
async def handle_delete_token_api_request(self, data: Dict) -> Dict:
return {"returnCode": "1"}
def handle_create_token_api_request(self, data: Dict) -> Dict:
async def handle_create_token_api_request(self, data: Dict) -> Dict:
return {"returnCode": "1"}
def handle_get_user_map_area_api_request(self, data: Dict) -> Dict:
async def handle_get_user_map_area_api_request(self, data: Dict) -> Dict:
user_map_areas = self.data.item.get_map_areas(data["userId"])
map_areas = []
@@ -113,10 +113,10 @@ class ChuniNew(ChuniBase):
return {"userId": data["userId"], "userMapAreaList": map_areas}
def handle_get_user_symbol_chat_setting_api_request(self, data: Dict) -> Dict:
async def handle_get_user_symbol_chat_setting_api_request(self, data: Dict) -> Dict:
return {"userId": data["userId"], "symbolCharInfoList": []}
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
async def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_preview(data["userId"], self.version)
if profile is None:
return None
@@ -164,7 +164,7 @@ class ChuniNew(ChuniBase):
}
return data1
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_data(data["userId"], self.version)
if p is None:
return {}
@@ -177,13 +177,13 @@ class ChuniNew(ChuniBase):
"isLogin": False,
}
def handle_printer_login_api_request(self, data: Dict) -> Dict:
async def handle_printer_login_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}
def handle_printer_logout_api_request(self, data: Dict) -> Dict:
async def handle_printer_logout_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}
def handle_get_game_gacha_api_request(self, data: Dict) -> Dict:
async def handle_get_game_gacha_api_request(self, data: Dict) -> Dict:
"""
returns all current active banners (gachas)
"""
@@ -213,7 +213,7 @@ class ChuniNew(ChuniBase):
"registIdList": [],
}
def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict:
async def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict:
"""
returns all valid cards for a given gachaId
"""
@@ -237,7 +237,7 @@ class ChuniNew(ChuniBase):
"ssrBookCalcList": [],
}
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
async def handle_cm_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 {}
@@ -262,7 +262,7 @@ class ChuniNew(ChuniBase):
],
}
def handle_get_user_gacha_api_request(self, data: Dict) -> Dict:
async def handle_get_user_gacha_api_request(self, data: Dict) -> Dict:
user_gachas = self.data.item.get_user_gachas(data["userId"])
if user_gachas is None:
return {"userId": data["userId"], "length": 0, "userGachaList": []}
@@ -281,7 +281,7 @@ class ChuniNew(ChuniBase):
"userGachaList": user_gacha_list,
}
def handle_get_user_printed_card_api_request(self, data: Dict) -> Dict:
async def handle_get_user_printed_card_api_request(self, data: Dict) -> Dict:
user_print_list = self.data.item.get_user_print_states(
data["userId"], has_completed=True
)
@@ -316,7 +316,7 @@ class ChuniNew(ChuniBase):
"userPrintedCardList": print_list,
}
def handle_get_user_card_print_error_api_request(self, data: Dict) -> Dict:
async def handle_get_user_card_print_error_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
user_print_states = self.data.item.get_user_print_states(
@@ -338,13 +338,13 @@ class ChuniNew(ChuniBase):
"userCardPrintStateList": card_print_state_list,
}
def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
return super().handle_get_user_character_api_request(data)
def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
return super().handle_get_user_item_api_request(data)
def handle_roll_gacha_api_request(self, data: Dict) -> Dict:
async def handle_roll_gacha_api_request(self, data: Dict) -> Dict:
"""
Handle a gacha roll API request, with:
gachaId: the gachaId where the cards should be pulled from
@@ -386,7 +386,7 @@ class ChuniNew(ChuniBase):
return {"length": len(rolled_cards), "gameGachaCardList": rolled_cards}
def handle_cm_upsert_user_gacha_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_gacha_api_request(self, data: Dict) -> Dict:
upsert = data["cmUpsertUserGacha"]
user_id = data["userId"]
place_id = data["placeId"]
@@ -441,7 +441,7 @@ class ChuniNew(ChuniBase):
"userCardPrintStateList": card_print_state_list,
}
def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
return {
"returnCode": 1,
"orderId": 0,
@@ -449,7 +449,7 @@ class ChuniNew(ChuniBase):
"apiName": "CMUpsertUserPrintlogApi",
}
def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
user_print_detail = data["userPrintDetail"]
user_id = data["userId"]
@@ -474,7 +474,7 @@ class ChuniNew(ChuniBase):
"apiName": "CMUpsertUserPrintApi",
}
def handle_cm_upsert_user_print_subtract_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_print_subtract_api_request(self, data: Dict) -> Dict:
upsert = data["userCardPrintState"]
user_id = data["userId"]
place_id = data["placeId"]
@@ -491,7 +491,7 @@ class ChuniNew(ChuniBase):
return {"returnCode": "1", "apiName": "CMUpsertUserPrintSubtractApi"}
def handle_cm_upsert_user_print_cancel_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_print_cancel_api_request(self, data: Dict) -> Dict:
order_ids = data["orderIdList"]
user_id = data["userId"]
@@ -501,11 +501,11 @@ class ChuniNew(ChuniBase):
return {"returnCode": "1", "apiName": "CMUpsertUserPrintCancelApi"}
def handle_ping_request(self, data: Dict) -> Dict:
async def handle_ping_request(self, data: Dict) -> Dict:
# matchmaking ping request
return {"returnCode": "1"}
def handle_begin_matching_api_request(self, data: Dict) -> Dict:
async def handle_begin_matching_api_request(self, data: Dict) -> Dict:
room_id = 1
# check if there is a free matching room
matching_room = self.data.item.get_oldest_free_matching(self.version)
@@ -554,7 +554,7 @@ class ChuniNew(ChuniBase):
return {"roomId": 1, "matchingWaitState": matching_wait}
def handle_end_matching_api_request(self, data: Dict) -> Dict:
async def handle_end_matching_api_request(self, data: Dict) -> Dict:
matching_room = self.data.item.get_matching(self.version, data["roomId"])
members = matching_room["matchingMemberInfoList"]
@@ -579,10 +579,10 @@ class ChuniNew(ChuniBase):
# no idea, maybe to differentiate between CPUs and real players?
"matchingMemberRoleList": role_list,
# TCP/UDP connection?
"reflectorUri": f"{self.core_cfg.title.hostname}",
"reflectorUri": f"{self.core_cfg.server.hostname}",
}
def handle_remove_matching_member_api_request(self, data: Dict) -> Dict:
async def handle_remove_matching_member_api_request(self, data: Dict) -> Dict:
# get all matching rooms, because Chuni only returns the userId
# not the actual roomId
matching_rooms = self.data.item.get_all_matchings(self.version)
@@ -612,7 +612,7 @@ class ChuniNew(ChuniBase):
return {"returnCode": "1"}
def handle_get_matching_state_api_request(self, data: Dict) -> Dict:
async def handle_get_matching_state_api_request(self, data: Dict) -> Dict:
polling_interval = 1
# get the current active room
matching_room = self.data.item.get_matching(self.version, data["roomId"])

View File

@@ -11,8 +11,8 @@ class ChuniNewPlus(ChuniNew):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_NEW_PLUS
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = super().handle_cm_get_user_preview_api_request(data)
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = await super().handle_cm_get_user_preview_api_request(data)
# hardcode lastDataVersion for CardMaker 1.35 A028
user_data["lastDataVersion"] = "2.05.00"

View File

@@ -13,7 +13,7 @@ class ChuniParadise(ChuniBase):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_PARADISE
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.50.00"
return ret

View File

@@ -11,7 +11,7 @@ class ChuniPlus(ChuniBase):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_PLUS
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.05.00"
return ret

View File

@@ -11,7 +11,7 @@ class ChuniStar(ChuniBase):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_STAR
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.20.00"
return ret

View File

@@ -11,7 +11,7 @@ class ChuniStarPlus(ChuniBase):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_STAR_PLUS
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.25.00"
return ret

View File

@@ -11,8 +11,8 @@ class ChuniSun(ChuniNewPlus):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_SUN
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = super().handle_cm_get_user_preview_api_request(data)
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = await super().handle_cm_get_user_preview_api_request(data)
# hardcode lastDataVersion for CardMaker 1.35 A032
user_data["lastDataVersion"] = "2.10.00"

View File

@@ -11,8 +11,8 @@ class ChuniSunPlus(ChuniSun):
super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_SUN_PLUS
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = super().handle_cm_get_user_preview_api_request(data)
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = await super().handle_cm_get_user_preview_api_request(data)
# I don't know if lastDataVersion is going to matter, I don't think CardMaker 1.35 works this far up
user_data["lastDataVersion"] = "2.15.00"

View File

@@ -29,11 +29,11 @@ class CardMakerBase:
def _parse_int_ver(version: str) -> str:
return version.replace(".", "")[:3]
def handle_get_game_connect_api_request(self, data: Dict) -> Dict:
async def handle_get_game_connect_api_request(self, data: Dict) -> Dict:
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
uri = f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}"
uri = f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}"
else:
uri = f"http://{self.core_cfg.title.hostname}"
uri = f"http://{self.core_cfg.server.hostname}"
# grab the dict with all games version numbers from user config
games_ver = self.game_cfg.version.version(self.version)
@@ -62,7 +62,7 @@ class CardMakerBase:
],
}
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
# if reboot start/end time is not defined use the default behavior of being a few hours ago
if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
reboot_start = datetime.strftime(
@@ -110,11 +110,11 @@ class CardMakerBase:
"isAou": False,
}
def handle_get_client_bookkeeping_api_request(self, data: Dict) -> Dict:
async def handle_get_client_bookkeeping_api_request(self, data: Dict) -> Dict:
return {"placeId": data["placeId"], "length": 0, "clientBookkeepingList": []}
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1, "apiName": "UpsertClientBookkeepingApi"}

View File

@@ -12,7 +12,7 @@ class CardMaker135(CardMakerBase):
super().__init__(core_cfg, game_cfg)
self.version = CardMakerConstants.VER_CARD_MAKER_135
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.35.00"
return ret

View File

@@ -5,10 +5,11 @@ import string
import logging
import coloredlogs
import zlib
from starlette.routing import Route
from starlette.responses import Response
from starlette.requests import Request
from os import path
from typing import Tuple, List, Dict
from twisted.web.http import Request
from typing import List
from logging.handlers import TimedRotatingFileHandler
from core.config import CoreConfig
@@ -19,7 +20,6 @@ from .const import CardMakerConstants
from .base import CardMakerBase
from .cm135 import CardMaker135
class CardMakerServlet(BaseServlet):
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
super().__init__(core_cfg, cfg_dir)
@@ -72,16 +72,15 @@ class CardMakerServlet(BaseServlet):
return True
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
return (
[],
[("render_POST", "/SDED/{version}/{endpoint}", {})]
)
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
version = int(matchers['version'])
endpoint = matchers['endpoint']
req_raw = request.content.getvalue()
def get_routes(self) -> List[Route]:
return [
Route("/SDED/{version:int}/{endpoint:str}", self.render_POST)
]
async def render_POST(self, request: Request) -> bytes:
version: int = request.path_params.get('version')
endpoint: str = request.path_params.get('endpoint')
req_raw = await request.body()
internal_ver = 0
client_ip = Utils.get_ip_addr(request)
@@ -103,7 +102,7 @@ class CardMakerServlet(BaseServlet):
self.logger.error(
f"Failed to decompress v{version} {endpoint} request -> {e}"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
req_data = json.loads(unzip)
@@ -114,7 +113,7 @@ class CardMakerServlet(BaseServlet):
if not hasattr(self.versions[internal_ver], func_to_find):
self.logger.warning(f"Unhandled v{version} request {endpoint}")
return zlib.compress(b'{"returnCode": 1}')
return Response(zlib.compress(b'{"returnCode": 1}'))
try:
handler = getattr(self.versions[internal_ver], func_to_find)
@@ -123,11 +122,11 @@ class CardMakerServlet(BaseServlet):
except Exception as e:
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
raise
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
if resp is None:
resp = {"returnCode": 1}
self.logger.debug(f"Response {resp}")
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
return Response(zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")))

View File

@@ -28,13 +28,13 @@ class CxbBase:
return []
def handle_action_rpreq_request(self, data: Dict) -> Dict:
async def handle_action_rpreq_request(self, data: Dict) -> Dict:
return {}
def handle_action_hitreq_request(self, data: Dict) -> Dict:
async def handle_action_hitreq_request(self, data: Dict) -> Dict:
return {"data": []}
def handle_auth_usercheck_request(self, data: Dict) -> Dict:
async def handle_auth_usercheck_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_index(
0, data["usercheck"]["authid"], self.version
)
@@ -45,11 +45,11 @@ class CxbBase:
self.logger.info(f"No profile for aime id {data['usercheck']['authid']}")
return {"exist": "false", "logout": "true"}
def handle_auth_entry_request(self, data: Dict) -> Dict:
async def handle_auth_entry_request(self, data: Dict) -> Dict:
self.logger.info(f"New profile for {data['entry']['authid']}")
return {"token": data["entry"]["authid"], "uid": data["entry"]["authid"]}
def handle_auth_login_request(self, data: Dict) -> Dict:
async def handle_auth_login_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_index(
0, data["login"]["authid"], self.version
)
@@ -198,7 +198,7 @@ class CxbBase:
).decode("utf-8")
)
def handle_action_loadrange_request(self, data: Dict) -> Dict:
async def handle_action_loadrange_request(self, data: Dict) -> Dict:
range_start = data["loadrange"]["range"][0]
range_end = data["loadrange"]["range"][1]
uid = data["loadrange"]["uid"]
@@ -282,7 +282,7 @@ class CxbBase:
return {"index": index, "data": data1, "version": versionindex}
def handle_action_saveindex_request(self, data: Dict) -> Dict:
async def handle_action_saveindex_request(self, data: Dict) -> Dict:
save_data = data["saveindex"]
try:
@@ -443,7 +443,7 @@ class CxbBase:
i += 1
return {}
def handle_action_sprankreq_request(self, data: Dict) -> Dict:
async def handle_action_sprankreq_request(self, data: Dict) -> Dict:
uid = data["sprankreq"]["uid"]
self.logger.info(f"Get best rankings for {uid}")
p = self.data.score.get_best_rankings(uid)
@@ -475,16 +475,16 @@ class CxbBase:
"rankx": [1, 1, 1],
}
def handle_action_getadv_request(self, data: Dict) -> Dict:
async def handle_action_getadv_request(self, data: Dict) -> Dict:
return {"data": [{"r": "1", "i": "100300", "c": "20"}]}
def handle_action_getmsg_request(self, data: Dict) -> Dict:
async def handle_action_getmsg_request(self, data: Dict) -> Dict:
return {"msgs": []}
def handle_auth_logout_request(self, data: Dict) -> Dict:
async def handle_auth_logout_request(self, data: Dict) -> Dict:
return {"auth": True}
def handle_action_rankreg_request(self, data: Dict) -> Dict:
async def handle_action_rankreg_request(self, data: Dict) -> Dict:
uid = data["rankreg"]["uid"]
self.logger.info(f"Put {len(data['rankreg']['data'])} rankings for {uid}")
@@ -527,7 +527,7 @@ class CxbBase:
)
return {}
def handle_action_addenergy_request(self, data: Dict) -> Dict:
async def handle_action_addenergy_request(self, data: Dict) -> Dict:
uid = data["addenergy"]["uid"]
self.logger.info(f"Add energy to user {uid}")
profile = self.data.profile.get_profile_index(0, uid, self.version)
@@ -570,10 +570,10 @@ class CxbBase:
)
return array[0]
def handle_action_eventreq_request(self, data: Dict) -> Dict:
async def handle_action_eventreq_request(self, data: Dict) -> Dict:
self.logger.info(data)
return {"eventreq": ""}
def handle_action_stampreq_request(self, data: Dict) -> Dict:
async def handle_action_stampreq_request(self, data: Dict) -> Dict:
self.logger.info(data)
return {"stampreq": ""}

View File

@@ -1,4 +1,6 @@
from twisted.web.http import Request
from starlette.requests import Request
from starlette.routing import Route
from starlette.responses import Response, PlainTextResponse, JSONResponse
import traceback
import sys
import yaml
@@ -62,6 +64,14 @@ class CxbServlet(BaseServlet):
CxbRevSunriseS2(core_cfg, self.game_cfg),
]
def get_routes(self) -> List[Route]:
return [
Route("/data", self.handle_data, methods=['POST']),
Route("/action", self.handle_action, methods=['POST']),
Route("/v2/action", self.handle_action, methods=['POST']),
Route("/auth", self.handle_auth, methods=['POST']),
]
@classmethod
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
game_cfg = CxbConfig()
@@ -88,26 +98,12 @@ class CxbServlet(BaseServlet):
t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else ""
return (
f"{proto}://{self.core_cfg.title.hostname}{t_port}",
f"{proto}://{self.core_cfg.server.hostname}{t_port}",
"",
)
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
return (
[],
[
("handle_data", "/data", {}),
("handle_action", "/action", {}),
("handle_action", "/v2/action", {}),
("handle_auth", "/auth", {}),
]
)
def preprocess(self, req: Request) -> Dict:
try:
req_bytes = req.content.getvalue()
except:
req_bytes = req.content.read() # Can we just use this one?
async def preprocess(self, req: Request) -> Dict:
req_bytes = await req.body()
try:
req_json: Dict = json.loads(req_bytes)
@@ -126,8 +122,8 @@ class CxbServlet(BaseServlet):
return req_json
def handle_data(self, request: Request, game_code: str, matchers: Dict) -> bytes:
req_json = self.preprocess(request)
async def handle_data(self, request: Request) -> bytes:
req_json = await self.preprocess(request)
func_to_find = "handle_data_"
version_string = "Base"
internal_ver = 0
@@ -135,7 +131,7 @@ class CxbServlet(BaseServlet):
if req_json == {}:
self.logger.warning(f"Empty json request to /data")
return b""
return Response()
subcmd = list(req_json.keys())[0]
if subcmd == "dldate":
@@ -145,14 +141,14 @@ class CxbServlet(BaseServlet):
or "filetype" not in req_json["dldate"]
):
self.logger.warning(f"Malformed dldate request: {req_json}")
return b""
return Response()
filetype = req_json["dldate"]["filetype"]
filetype_split = filetype.split("/")
if len(filetype_split) < 2 or not filetype_split[0].isnumeric():
self.logger.warning(f"Malformed dldate request: {req_json}")
return b""
return Response()
version = int(filetype_split[0])
filename = filetype_split[len(filetype_split) - 1]
@@ -184,7 +180,7 @@ class CxbServlet(BaseServlet):
if not hasattr(self.versions[internal_ver], func_to_find):
self.logger.warn(f"{version_string} has no handler for filetype {filetype} / {func_to_find}")
return({"data":""})
return JSONResponse({"data":""})
self.logger.info(f"{version_string} request for filetype {filetype}")
self.logger.debug(req_json)
@@ -192,7 +188,7 @@ class CxbServlet(BaseServlet):
handler = getattr(self.versions[internal_ver], func_to_find)
try:
resp = handler(req_json)
resp = await handler(req_json)
except Exception as e:
self.logger.error(f"Error handling request for file {filetype} - {e}")
@@ -201,19 +197,19 @@ class CxbServlet(BaseServlet):
traceback.print_exception(tp, val, tb, limit=1)
with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f:
traceback.print_exception(tp, val, tb, limit=1, file=f)
return ""
return Response()
self.logger.debug(f"{version_string} Response {resp}")
return json.dumps(resp, ensure_ascii=False).encode("utf-8")
return JSONResponse(resp, ensure_ascii=False)
def handle_action(self, request: Request, game_code: str, matchers: Dict) -> bytes:
req_json = self.preprocess(request)
async def handle_action(self, request: Request) -> bytes:
req_json = await self.preprocess(request)
subcmd = list(req_json.keys())[0]
func_to_find = f"handle_action_{subcmd}_request"
if not hasattr(self.versions[0], func_to_find):
self.logger.warn(f"No handler for action {subcmd} request")
return ""
return Response()
self.logger.info(f"Action {subcmd} Request")
self.logger.debug(req_json)
@@ -221,7 +217,7 @@ class CxbServlet(BaseServlet):
handler = getattr(self.versions[0], func_to_find)
try:
resp = handler(req_json)
resp = await handler(req_json)
except Exception as e:
self.logger.error(f"Error handling action {subcmd} request - {e}")
@@ -230,19 +226,19 @@ class CxbServlet(BaseServlet):
traceback.print_exception(tp, val, tb, limit=1)
with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f:
traceback.print_exception(tp, val, tb, limit=1, file=f)
return ""
return Response()
self.logger.debug(f"Response {resp}")
return json.dumps(resp, ensure_ascii=False).encode("utf-8")
return JSONResponse(resp)
def handle_auth(self, request: Request, game_code: str, matchers: Dict) -> bytes:
req_json = self.preprocess(request)
async def handle_auth(self, request: Request) -> bytes:
req_json = await self.preprocess(request)
subcmd = list(req_json.keys())[0]
func_to_find = f"handle_auth_{subcmd}_request"
if not hasattr(self.versions[0], func_to_find):
self.logger.warn(f"No handler for auth {subcmd} request")
return ""
return Response()
self.logger.info(f"Action {subcmd} Request")
self.logger.debug(req_json)
@@ -250,7 +246,7 @@ class CxbServlet(BaseServlet):
handler = getattr(self.versions[0], func_to_find)
try:
resp = handler(req_json)
resp = await handler(req_json)
except Exception as e:
self.logger.error(f"Error handling auth {subcmd} request - {e}")
@@ -259,7 +255,7 @@ class CxbServlet(BaseServlet):
traceback.print_exception(tp, val, tb, limit=1)
with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f:
traceback.print_exception(tp, val, tb, limit=1, file=f)
return ""
return Response()
self.logger.debug(f"Response {resp}")
return json.dumps(resp, ensure_ascii=False).encode("utf-8")
return JSONResponse(resp)

View File

@@ -17,10 +17,10 @@ class CxbRev(CxbBase):
super().__init__(cfg, game_cfg)
self.version = CxbConstants.VER_CROSSBEATS_REV
def handle_data_path_list_request(self, data: Dict) -> Dict:
async def handle_data_path_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_putlog_request(self, data: Dict) -> Dict:
async def handle_data_putlog_request(self, data: Dict) -> Dict:
if data["putlog"]["type"] == "ResultLog":
score_data = json.loads(data["putlog"]["data"])
userid = score_data["usid"]
@@ -45,7 +45,7 @@ class CxbRev(CxbBase):
return {"data": True}
@cached(lifetime=86400)
def handle_data_music_list_request(self, data: Dict) -> Dict:
async def handle_data_music_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(r"titles/cxb/data/rss/MusicArchiveList.csv") as music:
lines = music.readlines()
@@ -56,7 +56,7 @@ class CxbRev(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_item_list_icon_request(self, data: Dict) -> Dict:
async def handle_data_item_list_icon_request(self, data: Dict) -> Dict:
ret_str = "\r\n#ItemListIcon\r\n"
with open(
r"titles/cxb/data/rss/Item/ItemArchiveList_Icon.csv", encoding="utf-8"
@@ -67,7 +67,7 @@ class CxbRev(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_item_list_skin_notes_request(self, data: Dict) -> Dict:
async def handle_data_item_list_skin_notes_request(self, data: Dict) -> Dict:
ret_str = "\r\n#ItemListSkinNotes\r\n"
with open(
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinNotes.csv", encoding="utf-8"
@@ -78,7 +78,7 @@ class CxbRev(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_item_list_skin_effect_request(self, data: Dict) -> Dict:
async def handle_data_item_list_skin_effect_request(self, data: Dict) -> Dict:
ret_str = "\r\n#ItemListSkinEffect\r\n"
with open(
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinEffect.csv", encoding="utf-8"
@@ -89,7 +89,7 @@ class CxbRev(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_item_list_skin_bg_request(self, data: Dict) -> Dict:
async def handle_data_item_list_skin_bg_request(self, data: Dict) -> Dict:
ret_str = "\r\n#ItemListSkinBg\r\n"
with open(
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinBg.csv", encoding="utf-8"
@@ -100,7 +100,7 @@ class CxbRev(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_item_list_title_request(self, data: Dict) -> Dict:
async def handle_data_item_list_title_request(self, data: Dict) -> Dict:
ret_str = "\r\n#ItemListTitle\r\n"
with open(
r"titles/cxb/data/rss/Item/ItemList_Title.csv", encoding="shift-jis"
@@ -111,7 +111,7 @@ class CxbRev(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_shop_list_music_request(self, data: Dict) -> Dict:
async def handle_data_shop_list_music_request(self, data: Dict) -> Dict:
ret_str = "\r\n#ShopListMusic\r\n"
with open(
r"titles/cxb/data/rss/Shop/ShopList_Music.csv", encoding="shift-jis"
@@ -122,7 +122,7 @@ class CxbRev(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_shop_list_icon_request(self, data: Dict) -> Dict:
async def handle_data_shop_list_icon_request(self, data: Dict) -> Dict:
ret_str = "\r\n#ShopListIcon\r\n"
with open(
r"titles/cxb/data/rss/Shop/ShopList_Icon.csv", encoding="shift-jis"
@@ -133,7 +133,7 @@ class CxbRev(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_shop_list_title_request(self, data: Dict) -> Dict:
async def handle_data_shop_list_title_request(self, data: Dict) -> Dict:
ret_str = "\r\n#ShopListTitle\r\n"
with open(
r"titles/cxb/data/rss/Shop/ShopList_Title.csv", encoding="shift-jis"
@@ -143,17 +143,17 @@ class CxbRev(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_shop_list_skin_hud_request(self, data: Dict) -> Dict:
async def handle_data_shop_list_skin_hud_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_shop_list_skin_arrow_request(self, data: Dict) -> Dict:
async def handle_data_shop_list_skin_arrow_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_shop_list_skin_hit_request(self, data: Dict) -> Dict:
async def handle_data_shop_list_skin_hit_request(self, data: Dict) -> Dict:
return {"data": ""}
@cached(lifetime=86400)
def handle_data_shop_list_sale_request(self, data: Dict) -> Dict:
async def handle_data_shop_list_sale_request(self, data: Dict) -> Dict:
ret_str = "\r\n#ShopListSale\r\n"
with open(
r"titles/cxb/data/rss/Shop/ShopList_Sale.csv", encoding="shift-jis"
@@ -163,11 +163,11 @@ class CxbRev(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
async def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
return {"data": ""}
@cached(lifetime=86400)
def handle_data_exxxxx_request(self, data: Dict) -> Dict:
async def handle_data_exxxxx_request(self, data: Dict) -> Dict:
extra_num = int(data["dldate"]["filetype"][-4:])
ret_str = ""
with open(
@@ -178,14 +178,14 @@ class CxbRev(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
async def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
async def handle_data_free_coupon_request(self, data: Dict) -> Dict:
return {"data": ""}
@cached(lifetime=86400)
def handle_data_news_list_request(self, data: Dict) -> Dict:
async def handle_data_news_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(r"titles/cxb/data/rss/NewsList.csv", encoding="UTF-8") as news:
lines = news.readlines()
@@ -193,11 +193,11 @@ class CxbRev(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_tips_request(self, data: Dict) -> Dict:
async def handle_data_tips_request(self, data: Dict) -> Dict:
return {"data": ""}
@cached(lifetime=86400)
def handle_data_license_request(self, data: Dict) -> Dict:
async def handle_data_license_request(self, data: Dict) -> Dict:
ret_str = ""
with open(r"titles/cxb/data/rss/License_Offline.csv", encoding="UTF-8") as lic:
lines = lic.readlines()
@@ -206,7 +206,7 @@ class CxbRev(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_course_list_request(self, data: Dict) -> Dict:
async def handle_data_course_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(
r"titles/cxb/data/rss/Course/CourseList.csv", encoding="UTF-8"
@@ -217,7 +217,7 @@ class CxbRev(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_csxxxx_request(self, data: Dict) -> Dict:
async def handle_data_csxxxx_request(self, data: Dict) -> Dict:
# Removed the CSVs since the format isnt quite right
extra_num = int(data["dldate"]["filetype"][-4:])
ret_str = ""
@@ -230,7 +230,7 @@ class CxbRev(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_mission_list_request(self, data: Dict) -> Dict:
async def handle_data_mission_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(
r"titles/cxb/data/rss/MissionList.csv", encoding="shift-jis"
@@ -240,14 +240,14 @@ class CxbRev(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
async def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
async def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
return {"data": ""}
@cached(lifetime=86400)
def handle_data_event_list_request(self, data: Dict) -> Dict:
async def handle_data_event_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(
r"titles/cxb/data/rss/Event/EventArchiveList.csv", encoding="shift-jis"
@@ -257,39 +257,39 @@ class CxbRev(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_event_music_list_request(self, data: Dict) -> Dict:
async def handle_data_event_music_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_event_mission_list_request(self, data: Dict) -> Dict:
async def handle_data_event_mission_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_event_achievement_single_high_score_list_request(
async def handle_data_event_achievement_single_high_score_list_request(
self, data: Dict
) -> Dict:
return {"data": ""}
def handle_data_event_achievement_single_accumulation_request(
async def handle_data_event_achievement_single_accumulation_request(
self, data: Dict
) -> Dict:
return {"data": ""}
def handle_data_event_ranking_high_score_list_request(self, data: Dict) -> Dict:
async def handle_data_event_ranking_high_score_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_event_ranking_accumulation_list_request(self, data: Dict) -> Dict:
async def handle_data_event_ranking_accumulation_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_event_ranking_stamp_list_request(self, data: Dict) -> Dict:
async def handle_data_event_ranking_stamp_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_event_ranking_store_list_request(self, data: Dict) -> Dict:
async def handle_data_event_ranking_store_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_event_ranking_area_list_request(self, data: Dict) -> Dict:
async def handle_data_event_ranking_area_list_request(self, data: Dict) -> Dict:
return {"data": ""}
@cached(lifetime=86400)
def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
async def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(
r"titles/cxb/data/rss/Event/EventStampList.csv", encoding="shift-jis"
@@ -299,8 +299,8 @@ class CxbRev(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
async def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
def handle_data_server_state_request(self, data: Dict) -> Dict:
async def handle_data_server_state_request(self, data: Dict) -> Dict:
return {"data": True}

View File

@@ -17,11 +17,11 @@ class CxbRevSunriseS1(CxbBase):
super().__init__(cfg, game_cfg)
self.version = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S1
def handle_data_path_list_request(self, data: Dict) -> Dict:
async def handle_data_path_list_request(self, data: Dict) -> Dict:
return {"data": ""}
@cached(lifetime=86400)
def handle_data_music_list_request(self, data: Dict) -> Dict:
async def handle_data_music_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(r"titles/cxb/data/rss1/MusicArchiveList.csv") as music:
lines = music.readlines()
@@ -32,7 +32,7 @@ class CxbRevSunriseS1(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_item_list_detail_request(self, data: Dict) -> Dict:
async def handle_data_item_list_detail_request(self, data: Dict) -> Dict:
# ItemListIcon load
ret_str = "#ItemListIcon\r\n"
with open(
@@ -54,7 +54,7 @@ class CxbRevSunriseS1(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_shop_list_detail_request(self, data: Dict) -> Dict:
async def handle_data_shop_list_detail_request(self, data: Dict) -> Dict:
# ShopListIcon load
ret_str = "#ShopListIcon\r\n"
with open(
@@ -119,26 +119,26 @@ class CxbRevSunriseS1(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
async def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_exxxxx_request(self, data: Dict) -> Dict:
async def handle_data_exxxxx_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
async def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
async def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_oexxxx_request(self, data: Dict) -> Dict:
async def handle_data_oexxxx_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
async def handle_data_free_coupon_request(self, data: Dict) -> Dict:
return {"data": ""}
@cached(lifetime=86400)
def handle_data_news_list_request(self, data: Dict) -> Dict:
async def handle_data_news_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(r"titles/cxb/data/rss1/NewsList.csv", encoding="UTF-8") as news:
lines = news.readlines()
@@ -146,14 +146,14 @@ class CxbRevSunriseS1(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_tips_request(self, data: Dict) -> Dict:
async def handle_data_tips_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_release_info_list_request(self, data: Dict) -> Dict:
async def handle_data_release_info_list_request(self, data: Dict) -> Dict:
return {"data": ""}
@cached(lifetime=86400)
def handle_data_random_music_list_request(self, data: Dict) -> Dict:
async def handle_data_random_music_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(r"titles/cxb/data/rss1/MusicArchiveList.csv") as music:
lines = music.readlines()
@@ -167,7 +167,7 @@ class CxbRevSunriseS1(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_license_request(self, data: Dict) -> Dict:
async def handle_data_license_request(self, data: Dict) -> Dict:
ret_str = ""
with open(r"titles/cxb/data/rss1/License.csv", encoding="UTF-8") as licenses:
lines = licenses.readlines()
@@ -176,7 +176,7 @@ class CxbRevSunriseS1(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_course_list_request(self, data: Dict) -> Dict:
async def handle_data_course_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(
r"titles/cxb/data/rss1/Course/CourseList.csv", encoding="UTF-8"
@@ -187,7 +187,7 @@ class CxbRevSunriseS1(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_csxxxx_request(self, data: Dict) -> Dict:
async def handle_data_csxxxx_request(self, data: Dict) -> Dict:
extra_num = int(data["dldate"]["filetype"][-4:])
ret_str = ""
with open(
@@ -198,16 +198,16 @@ class CxbRevSunriseS1(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_mission_list_request(self, data: Dict) -> Dict:
async def handle_data_mission_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
async def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
async def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_partner_list_request(self, data: Dict) -> Dict:
async def handle_data_partner_list_request(self, data: Dict) -> Dict:
ret_str = ""
# Lord forgive me for the sins I am about to commit
for i in range(0, 10):
@@ -226,7 +226,7 @@ class CxbRevSunriseS1(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_partnerxxxx_request(self, data: Dict) -> Dict:
async def handle_data_partnerxxxx_request(self, data: Dict) -> Dict:
partner_num = int(data["dldate"]["filetype"][-4:])
ret_str = f"{partner_num},,{partner_num},1,10000,\r\n"
with open(r"titles/cxb/data/rss1/Partner0000.csv") as partner:
@@ -235,13 +235,13 @@ class CxbRevSunriseS1(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_server_state_request(self, data: Dict) -> Dict:
async def handle_data_server_state_request(self, data: Dict) -> Dict:
return {"data": True}
def handle_data_settings_request(self, data: Dict) -> Dict:
async def handle_data_settings_request(self, data: Dict) -> Dict:
return {"data": "2,\r\n"}
def handle_data_story_list_request(self, data: Dict) -> Dict:
async def handle_data_story_list_request(self, data: Dict) -> Dict:
# story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu
ret_str = "\r\n"
ret_str += (
@@ -253,23 +253,23 @@ class CxbRevSunriseS1(CxbBase):
ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n"
return {"data": ret_str}
def handle_data_stxxxx_request(self, data: Dict) -> Dict:
async def handle_data_stxxxx_request(self, data: Dict) -> Dict:
story_num = int(data["dldate"]["filetype"][-4:])
ret_str = ""
for i in range(1, 11):
ret_str += f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n"
return {"data": ret_str}
def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
async def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
return {"data": "Cs1032,1,1,1,1,1,1,1,1,1,1,\r\n"}
def handle_data_premium_list_request(self, data: Dict) -> Dict:
async def handle_data_premium_list_request(self, data: Dict) -> Dict:
return {"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"}
def handle_data_event_list_request(self, data: Dict) -> Dict:
async def handle_data_event_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_event_detail_list_request(self, data: Dict) -> Dict:
async def handle_data_event_detail_list_request(self, data: Dict) -> Dict:
event_id = data["dldate"]["filetype"].split("/")[2]
if "EventStampMapListCs1002" in event_id:
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
@@ -278,7 +278,7 @@ class CxbRevSunriseS1(CxbBase):
else:
return {"data": ""}
def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
async def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
event_id = data["dldate"]["filetype"].split("/")[2]
if "EventStampMapListCs1002" in event_id:
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}

View File

@@ -17,11 +17,11 @@ class CxbRevSunriseS2(CxbBase):
super().__init__(cfg, game_cfg)
self.version = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S2_OMNI
def handle_data_path_list_request(self, data: Dict) -> Dict:
async def handle_data_path_list_request(self, data: Dict) -> Dict:
return {"data": ""}
@cached(lifetime=86400)
def handle_data_music_list_request(self, data: Dict) -> Dict:
async def handle_data_music_list_request(self, data: Dict) -> Dict:
version = data["dldate"]["filetype"].split("/")[0]
ret_str = ""
@@ -41,7 +41,7 @@ class CxbRevSunriseS2(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_item_list_detail_request(self, data: Dict) -> Dict:
async def handle_data_item_list_detail_request(self, data: Dict) -> Dict:
# ItemListIcon load
ret_str = "#ItemListIcon\r\n"
with open(
@@ -63,7 +63,7 @@ class CxbRevSunriseS2(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_shop_list_detail_request(self, data: Dict) -> Dict:
async def handle_data_shop_list_detail_request(self, data: Dict) -> Dict:
# ShopListIcon load
ret_str = "#ShopListIcon\r\n"
with open(
@@ -128,7 +128,7 @@ class CxbRevSunriseS2(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
async def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
ret_str=""
with open(r"titles/cxb/data/rss2/ExtraStageList.csv") as extra:
lines = extra.readlines()
@@ -136,19 +136,19 @@ class CxbRevSunriseS2(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return({"data":ret_str})
def handle_data_exxxxx_request(self, data: Dict) -> Dict:
async def handle_data_exxxxx_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
async def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
async def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_oexxxx_request(self, data: Dict) -> Dict:
async def handle_data_oexxxx_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
async def handle_data_free_coupon_request(self, data: Dict) -> Dict:
ret_str=""
with open(r"titles/cxb/data/rss2/FreeCoupon.csv") as coupon:
lines = coupon.readlines()
@@ -157,7 +157,7 @@ class CxbRevSunriseS2(CxbBase):
return({"data":ret_str})
@cached(lifetime=86400)
def handle_data_news_list_request(self, data: Dict) -> Dict:
async def handle_data_news_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(r"titles/cxb/data/rss2/NewsList.csv", encoding="UTF-8") as news:
lines = news.readlines()
@@ -165,14 +165,14 @@ class CxbRevSunriseS2(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_tips_request(self, data: Dict) -> Dict:
async def handle_data_tips_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_release_info_list_request(self, data: Dict) -> Dict:
async def handle_data_release_info_list_request(self, data: Dict) -> Dict:
return {"data": ""}
@cached(lifetime=86400)
def handle_data_random_music_list_request(self, data: Dict) -> Dict:
async def handle_data_random_music_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(r"titles/cxb/data/rss2/MusicArchiveList.csv") as music:
lines = music.readlines()
@@ -186,7 +186,7 @@ class CxbRevSunriseS2(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_license_request(self, data: Dict) -> Dict:
async def handle_data_license_request(self, data: Dict) -> Dict:
ret_str = ""
with open(r"titles/cxb/data/rss2/License.csv", encoding="UTF-8") as licenses:
lines = licenses.readlines()
@@ -195,7 +195,7 @@ class CxbRevSunriseS2(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_course_list_request(self, data: Dict) -> Dict:
async def handle_data_course_list_request(self, data: Dict) -> Dict:
ret_str = ""
with open(
r"titles/cxb/data/rss2/Course/CourseList.csv", encoding="UTF-8"
@@ -206,7 +206,7 @@ class CxbRevSunriseS2(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_csxxxx_request(self, data: Dict) -> Dict:
async def handle_data_csxxxx_request(self, data: Dict) -> Dict:
extra_num = int(data["dldate"]["filetype"][-4:])
ret_str = ""
with open(
@@ -217,16 +217,16 @@ class CxbRevSunriseS2(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_mission_list_request(self, data: Dict) -> Dict:
async def handle_data_mission_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
async def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
async def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_partner_list_request(self, data: Dict) -> Dict:
async def handle_data_partner_list_request(self, data: Dict) -> Dict:
ret_str = ""
# Lord forgive me for the sins I am about to commit
for i in range(0, 10):
@@ -245,7 +245,7 @@ class CxbRevSunriseS2(CxbBase):
return {"data": ret_str}
@cached(lifetime=86400)
def handle_data_partnerxxxx_request(self, data: Dict) -> Dict:
async def handle_data_partnerxxxx_request(self, data: Dict) -> Dict:
partner_num = int(data["dldate"]["filetype"][-4:])
ret_str = f"{partner_num},,{partner_num},1,10000,\r\n"
with open(r"titles/cxb/data/rss2/Partner0000.csv") as partner:
@@ -254,13 +254,13 @@ class CxbRevSunriseS2(CxbBase):
ret_str += f"{line[:-1]}\r\n"
return {"data": ret_str}
def handle_data_server_state_request(self, data: Dict) -> Dict:
async def handle_data_server_state_request(self, data: Dict) -> Dict:
return {"data": True}
def handle_data_settings_request(self, data: Dict) -> Dict:
async def handle_data_settings_request(self, data: Dict) -> Dict:
return {"data": "2,\r\n"}
def handle_data_story_list_request(self, data: Dict) -> Dict:
async def handle_data_story_list_request(self, data: Dict) -> Dict:
# story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu
ret_str = "\r\n"
ret_str += (
@@ -272,7 +272,7 @@ class CxbRevSunriseS2(CxbBase):
ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n"
return {"data": ret_str}
def handle_data_stxxxx_request(self, data: Dict) -> Dict:
async def handle_data_stxxxx_request(self, data: Dict) -> Dict:
story_num = int(data["dldate"]["filetype"][-4:])
ret_str = ""
# Each stories appears to have 10 pieces based on the wiki but as on how they are set.... no clue
@@ -280,18 +280,18 @@ class CxbRevSunriseS2(CxbBase):
ret_str += f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n"
return {"data": ret_str}
def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
async def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
return {"data": "Cs1002,1,1,1,1,1,1,1,1,1,1,\r\n"}
def handle_data_premium_list_request(self, data: Dict) -> Dict:
async def handle_data_premium_list_request(self, data: Dict) -> Dict:
return {"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"}
def handle_data_event_list_request(self, data: Dict) -> Dict:
async def handle_data_event_list_request(self, data: Dict) -> Dict:
return {
"data": "Cs4001,0,10000,1601510400,1604188799,1,nv2006,1,\r\nCs4005,0,10000,1609459200,1615766399,1,nv2006,1,\r\n"
}
def handle_data_event_detail_list_request(self, data: Dict) -> Dict:
async def handle_data_event_detail_list_request(self, data: Dict) -> Dict:
event_id = data["dldate"]["filetype"].split("/")[2]
if "Cs4001" in event_id:
return {
@@ -308,7 +308,7 @@ class CxbRevSunriseS2(CxbBase):
else:
return {"data": ""}
def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
async def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
event_id = data["dldate"]["filetype"].split("/")[2]
if "EventStampMapListCs1002" in event_id:
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}

View File

@@ -1,8 +1,7 @@
import datetime
from typing import Any, List, Dict
from typing import Dict
import logging
import json
import urllib
import urllib.parse
from threading import Thread
from core.config import CoreConfig
@@ -24,13 +23,13 @@ class DivaBase:
dt = datetime.datetime.now()
self.time_lut = urllib.parse.quote(dt.strftime("%Y-%m-%d %H:%M:%S:16.0"))
def handle_test_request(self, data: Dict) -> Dict:
async def handle_test_request(self, data: Dict) -> Dict:
return ""
def handle_game_init_request(self, data: Dict) -> Dict:
async def handle_game_init_request(self, data: Dict) -> Dict:
return f""
def handle_attend_request(self, data: Dict) -> Dict:
async def handle_attend_request(self, data: Dict) -> Dict:
encoded = "&"
params = {
"atnd_prm1": "0,1,1,0,0,0,1,0,100,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
@@ -44,7 +43,7 @@ class DivaBase:
return encoded
def handle_ping_request(self, data: Dict) -> Dict:
async def handle_ping_request(self, data: Dict) -> Dict:
encoded = "&"
params = {
"ping_b_msg": f"Welcome to {self.core_cfg.server.name} network!",
@@ -89,7 +88,7 @@ class DivaBase:
return encoded
def handle_pv_list_request(self, data: Dict) -> Dict:
async def handle_pv_list_request(self, data: Dict) -> Dict:
pvlist = ""
with open(r"titles/diva/data/PvList0.dat", encoding="utf-8") as shop:
lines = shop.readlines()
@@ -126,7 +125,7 @@ class DivaBase:
return response
def handle_shop_catalog_request(self, data: Dict) -> Dict:
async def handle_shop_catalog_request(self, data: Dict) -> Dict:
catalog = ""
shopList = self.data.static.get_enabled_shops(self.version)
@@ -164,7 +163,7 @@ class DivaBase:
return response
def handle_buy_module_request(self, data: Dict) -> Dict:
async def handle_buy_module_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
module = self.data.static.get_enabled_shop(self.version, int(data["mdl_id"]))
@@ -191,7 +190,7 @@ class DivaBase:
return response
def handle_cstmz_itm_ctlg_request(self, data: Dict) -> Dict:
async def handle_cstmz_itm_ctlg_request(self, data: Dict) -> Dict:
catalog = ""
itemList = self.data.static.get_enabled_items(self.version)
@@ -229,7 +228,7 @@ class DivaBase:
return response
def handle_buy_cstmz_itm_request(self, data: Dict) -> Dict:
async def handle_buy_cstmz_itm_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
item = self.data.static.get_enabled_item(
self.version, int(data["cstmz_itm_id"])
@@ -264,7 +263,7 @@ class DivaBase:
return response
def handle_festa_info_request(self, data: Dict) -> Dict:
async def handle_festa_info_request(self, data: Dict) -> Dict:
encoded = "&"
params = {
"fi_id": "1,2",
@@ -287,7 +286,7 @@ class DivaBase:
return encoded
def handle_contest_info_request(self, data: Dict) -> Dict:
async def handle_contest_info_request(self, data: Dict) -> Dict:
response = ""
response += f"&ci_lut={self.time_lut}"
@@ -295,7 +294,7 @@ class DivaBase:
return response
def handle_qst_inf_request(self, data: Dict) -> Dict:
async def handle_qst_inf_request(self, data: Dict) -> Dict:
quest = ""
questList = self.data.static.get_enabled_quests(self.version)
@@ -345,43 +344,43 @@ class DivaBase:
return response
def handle_nv_ranking_request(self, data: Dict) -> Dict:
async def handle_nv_ranking_request(self, data: Dict) -> Dict:
return f""
def handle_ps_ranking_request(self, data: Dict) -> Dict:
async def handle_ps_ranking_request(self, data: Dict) -> Dict:
return f""
def handle_ng_word_request(self, data: Dict) -> Dict:
async def handle_ng_word_request(self, data: Dict) -> Dict:
return f""
def handle_rmt_wp_list_request(self, data: Dict) -> Dict:
async def handle_rmt_wp_list_request(self, data: Dict) -> Dict:
return f""
def handle_pv_def_chr_list_request(self, data: Dict) -> Dict:
async def handle_pv_def_chr_list_request(self, data: Dict) -> Dict:
return f""
def handle_pv_ng_mdl_list_request(self, data: Dict) -> Dict:
async def handle_pv_ng_mdl_list_request(self, data: Dict) -> Dict:
return f""
def handle_cstmz_itm_ng_mdl_lst_request(self, data: Dict) -> Dict:
async def handle_cstmz_itm_ng_mdl_lst_request(self, data: Dict) -> Dict:
return f""
def handle_banner_info_request(self, data: Dict) -> Dict:
async def handle_banner_info_request(self, data: Dict) -> Dict:
return f""
def handle_banner_data_request(self, data: Dict) -> Dict:
async def handle_banner_data_request(self, data: Dict) -> Dict:
return f""
def handle_cm_ply_info_request(self, data: Dict) -> Dict:
async def handle_cm_ply_info_request(self, data: Dict) -> Dict:
return f""
def handle_pstd_h_ctrl_request(self, data: Dict) -> Dict:
async def handle_pstd_h_ctrl_request(self, data: Dict) -> Dict:
return f""
def handle_pstd_item_ng_lst_request(self, data: Dict) -> Dict:
async def handle_pstd_item_ng_lst_request(self, data: Dict) -> Dict:
return f""
def handle_pre_start_request(self, data: Dict) -> str:
async def handle_pre_start_request(self, data: Dict) -> str:
profile = self.data.profile.get_profile(data["aime_id"], self.version)
profile_shop = self.data.item.get_shop(data["aime_id"], self.version)
@@ -422,13 +421,13 @@ class DivaBase:
return response
def handle_registration_request(self, data: Dict) -> Dict:
async def handle_registration_request(self, data: Dict) -> Dict:
self.data.profile.create_profile(
self.version, data["aime_id"], data["player_name"]
)
return f"&cd_adm_result=1&pd_id={data['aime_id']}"
def handle_start_request(self, data: Dict) -> Dict:
async def handle_start_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
profile_shop = self.data.item.get_shop(data["pd_id"], self.version)
if profile is None:
@@ -583,10 +582,10 @@ class DivaBase:
return response
def handle_pd_unlock_request(self, data: Dict) -> Dict:
async def handle_pd_unlock_request(self, data: Dict) -> Dict:
return f""
def handle_spend_credit_request(self, data: Dict) -> Dict:
async def handle_spend_credit_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
if profile is None:
return
@@ -705,7 +704,7 @@ class DivaBase:
pd_by_pv_id.append(urllib.parse.quote(f"{song}***"))
pd_by_pv_id.append(",")
def handle_get_pv_pd_request(self, data: Dict) -> Dict:
async def handle_get_pv_pd_request(self, data: Dict) -> Dict:
song_id = data["pd_pv_id_lst"].split(",")
pv = ""
@@ -732,10 +731,10 @@ class DivaBase:
return response
def handle_stage_start_request(self, data: Dict) -> Dict:
async def handle_stage_start_request(self, data: Dict) -> Dict:
return f""
def handle_stage_result_request(self, data: Dict) -> Dict:
async def handle_stage_result_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
pd_song_list = data["stg_ply_pv_id"].split(",")
@@ -914,7 +913,7 @@ class DivaBase:
return response
def handle_end_request(self, data: Dict) -> Dict:
async def handle_end_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
self.data.profile.update_profile(
@@ -922,7 +921,7 @@ class DivaBase:
)
return f""
def handle_shop_exit_request(self, data: Dict) -> Dict:
async def handle_shop_exit_request(self, data: Dict) -> Dict:
self.data.item.put_shop(
data["pd_id"],
self.version,
@@ -952,7 +951,7 @@ class DivaBase:
response = "&shp_rslt=1"
return response
def handle_card_procedure_request(self, data: Dict) -> str:
async def handle_card_procedure_request(self, data: Dict) -> str:
profile = self.data.profile.get_profile(data["aime_id"], self.version)
if profile is None:
return "&cd_adm_result=0"
@@ -972,7 +971,7 @@ class DivaBase:
return response
def handle_change_name_request(self, data: Dict) -> str:
async def handle_change_name_request(self, data: Dict) -> str:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
# make sure user has enough Vocaloid Points
@@ -992,7 +991,7 @@ class DivaBase:
return response
def handle_change_passwd_request(self, data: Dict) -> str:
async def handle_change_passwd_request(self, data: Dict) -> str:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
# TODO: return correct error number instead of 0

View File

@@ -1,4 +1,6 @@
from twisted.web.http import Request
from starlette.requests import Request
from starlette.responses import PlainTextResponse
from starlette.routing import Route
import yaml
import logging, coloredlogs
from logging.handlers import TimedRotatingFileHandler
@@ -51,17 +53,16 @@ class DivaServlet(BaseServlet):
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
)
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
return (
[],
[("render_POST", "/DivaServlet/", {})]
)
def get_routes(self) -> List[Route]:
return [
Route("/DivaServlet/", self.render_POST, methods=['POST'])
]
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/DivaServlet/", self.core_cfg.title.hostname)
return (f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/DivaServlet/", self.core_cfg.server.hostname)
return (f"http://{self.core_cfg.title.hostname}/DivaServlet/", self.core_cfg.title.hostname)
return (f"http://{self.core_cfg.server.hostname}/DivaServlet/", self.core_cfg.server.hostname)
@classmethod
def is_game_enabled(
@@ -78,9 +79,9 @@ class DivaServlet(BaseServlet):
return True
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
req_raw = request.content.getvalue()
url_header = request.getAllHeaders()
async def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
req_raw = await request.body()
url_header = request.headers
# Ping Dispatch
if "THIS_STRING_SEPARATES" in str(url_header):
@@ -103,9 +104,7 @@ class DivaServlet(BaseServlet):
self.logger.debug(
f"Response cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}"
)
return f"cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}".encode(
"utf-8"
)
return PlainTextResponse(f"cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}")
# Main Dispatch
json_string = json.dumps(
@@ -122,7 +121,7 @@ class DivaServlet(BaseServlet):
) # Decompressing the gzip
except zlib.error as e:
self.logger.error(f"Failed to defalte! {e} -> {gz_string}")
return "stat=0"
return PlainTextResponse("stat=0")
req_kvp = urllib.parse.unquote(url_data)
req_data = {}
@@ -141,27 +140,18 @@ class DivaServlet(BaseServlet):
# Load the requests
try:
handler = getattr(self.base, func_to_find)
resp = handler(req_data)
resp = await handler(req_data)
except AttributeError as e:
self.logger.warning(f"Unhandled {req_data['cmd']} request {e}")
return f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok".encode(
"utf-8"
)
return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok")
except Exception as e:
self.logger.error(f"Error handling method {func_to_find} {e}")
return f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok".encode(
"utf-8"
)
return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok")
request.responseHeaders.addRawHeader(b"content-type", b"text/plain")
self.logger.debug(
f"Response cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}"
)
return (
f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}".encode(
"utf-8"
)
)
return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}")

View File

@@ -3,7 +3,7 @@ import yaml
import jinja2
from os import path
from twisted.web.util import redirectTo
from twisted.web.http import Request
from starlette.requests import Request
from twisted.web.server import Session
from core.frontend import FE_Base, IUserSession

View File

@@ -1,18 +1,18 @@
import json
import traceback
import inflection
from starlette.routing import Route
from starlette.requests import Request
from starlette.responses import JSONResponse
import yaml
import logging
import coloredlogs
from os import path
from typing import Dict, List, Tuple
from logging.handlers import TimedRotatingFileHandler
from twisted.web import server
from twisted.web.http import Request
from twisted.internet import reactor, endpoints
from core.config import CoreConfig
from core.title import BaseServlet
from core.utils import Utils
from titles.idac.base import IDACBase
from titles.idac.season2 import IDACSeason2
@@ -22,7 +22,7 @@ from titles.idac.echo import IDACEchoUDP
from titles.idac.matching import IDACMatching
class IDACServlet:
class IDACServlet(BaseServlet):
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
self.core_cfg = core_cfg
self.game_cfg = IDACConfig()
@@ -72,12 +72,11 @@ class IDACServlet:
return False
return True
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
return (
[],
[("render_POST", "/SDGT/{version}/initiald/{category}/{endpoint}", {})]
)
def get_routes(self) -> List[Route]:
return [
Route("/{version:int}/initiald/{category:str}/{endpoint:str}", self.render_POST, methods=["POST"])
]
def get_allnet_info(
self, game_code: str, game_ver: int, keychip: str
@@ -88,15 +87,15 @@ class IDACServlet:
return (
f"",
# requires http or else it defaults to https
f"http://{self.core_cfg.title.hostname}{t_port}/{game_code}/{game_ver}/",
f"http://{self.core_cfg.server.hostname}{t_port}/{game_code}/{game_ver}/",
)
def render_POST(self, request: Request, game_code: int, matchers: Dict) -> bytes:
req_raw = request.content.getvalue()
async def render_POST(self, request: Request) -> bytes:
req_raw = await request.body()
internal_ver = 0
version = int(matchers['version'])
category = matchers['category']
endpoint = matchers['endpoint']
version: int = request.path_params.get('version')
category: str = request.path_params.get('category')
endpoint: str = request.path_params.get('endpoint')
client_ip = Utils.get_ip_addr(request)
if version >= 100 and version < 140: # IDAC Season 1
@@ -104,7 +103,7 @@ class IDACServlet:
elif version >= 140 and version < 171: # IDAC Season 2
internal_ver = IDACConstants.VER_IDAC_SEASON_2
header_application = self.decode_header(request.getAllHeaders())
header_application = self.decode_header(request.headers.get("application", ""))
req_data = json.loads(req_raw)
@@ -119,7 +118,7 @@ class IDACServlet:
if not hasattr(self.versions[internal_ver], func_to_find):
self.logger.warning(f"Unhandled v{version} request {endpoint}")
return '{"status_code": "0"}'.encode("utf-8")
return JSONResponse('{"status_code": "0"}')
resp = None
try:
@@ -129,17 +128,16 @@ class IDACServlet:
except Exception as e:
traceback.print_exc()
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
return '{"status_code": "0"}'.encode("utf-8")
return JSONResponse('{"status_code": "0"}')
if resp is None:
resp = {"status_code": "0"}
self.logger.debug(f"Response {resp}")
return json.dumps(resp, ensure_ascii=False).encode("utf-8")
return JSONResponse(json.dumps(resp, ensure_ascii=False))
def decode_header(self, data: Dict) -> Dict:
app: str = data[b"application"].decode()
def decode_header(self, app: str) -> Dict:
ret = {}
for x in app.split(", "):
@@ -149,6 +147,8 @@ class IDACServlet:
return ret
def setup(self):
return
"""
if self.game_cfg.server.enable:
endpoints.serverFromString(
reactor,
@@ -165,3 +165,4 @@ class IDACServlet:
)
self.logger.info(f"Matching listening on {self.game_cfg.server.matching} with echos on {self.game_cfg.server.echo1} and {self.game_cfg.server.echo2}")
"""

View File

@@ -109,9 +109,9 @@ class IDACSeason2(IDACBase):
ver_str = version.replace(".", "")[:3]
if self.core_cfg.server.is_develop:
domain_api_game = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDGT/{ver_str}/"
domain_api_game = f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/SDGT/{ver_str}/"
else:
domain_api_game = f"http://{self.core_cfg.title.hostname}/SDGT/{ver_str}/"
domain_api_game = f"http://{self.core_cfg.server.hostname}/SDGT/{ver_str}/"
return {
"status_code": "0",
@@ -136,10 +136,10 @@ class IDACSeason2(IDACBase):
"server_maintenance_end_hour": 0,
"server_maintenance_end_minutes": 0,
"domain_api_game": domain_api_game,
"domain_matching": f"http://{self.core_cfg.title.hostname}:{self.game_config.server.matching}",
"domain_echo1": f"{self.core_cfg.title.hostname}:{self.game_config.server.echo1}",
"domain_echo2": f"{self.core_cfg.title.hostname}:{self.game_config.server.echo2}",
"domain_ping": f"{self.core_cfg.title.hostname}",
"domain_matching": f"http://{self.core_cfg.server.hostname}:{self.game_config.server.matching}",
"domain_echo1": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo1}",
"domain_echo2": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo2}",
"domain_ping": f"{self.core_cfg.server.hostname}",
"battle_gift_event_master": [],
"round_event": [
{

View File

@@ -24,10 +24,10 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
t_port = Utils.get_title_port(self.core_config)
news_str = f"http://{self.core_config.title.hostname}:{t_port}/idz/news/news80**.txt"
err_str = f"http://{self.core_config.title.hostname}:{t_port}/idz/error"
news_str = f"http://{self.core_config.server.hostname}:{t_port}/idz/news/news80**.txt"
err_str = f"http://{self.core_config.server.hostname}:{t_port}/idz/error"
len_hostname = len(self.core_config.title.hostname)
len_hostname = len(self.core_config.server.hostname)
len_news = len(news_str)
len_error = len(err_str)
@@ -36,7 +36,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
f"{len_hostname}s",
ret,
0x4 + offset,
self.core_config.title.hostname.encode(),
self.core_config.server.hostname.encode(),
)
struct.pack_into("<I", ret, 0x84 + offset, self.game_cfg.ports.userdb)
struct.pack_into("<I", ret, 0x86 + offset, self.game_cfg.ports.userdb + 1)
@@ -45,7 +45,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
f"{len_hostname}s",
ret,
0x88 + offset,
self.core_config.title.hostname.encode(),
self.core_config.server.hostname.encode(),
)
struct.pack_into("<I", ret, 0x108 + offset, self.game_cfg.ports.match - 1)
struct.pack_into("<I", ret, 0x10A + offset, self.game_cfg.ports.match - 3)
@@ -59,7 +59,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
f"{len_hostname}s",
ret,
0x114 + offset,
self.core_config.title.hostname.encode(),
self.core_config.server.hostname.encode(),
)
struct.pack_into("<I", ret, 0x194 + offset, self.game_cfg.ports.echo + 2)
@@ -67,7 +67,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
f"{len_hostname}s",
ret,
0x0199 + offset,
self.core_config.title.hostname.encode(),
self.core_config.server.hostname.encode(),
)
struct.pack_into("<I", ret, 0x0219 + offset, self.game_cfg.ports.echo + 3)
@@ -75,19 +75,19 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
f"{len_hostname}s",
ret,
0x021C + offset,
self.core_config.title.hostname.encode(),
self.core_config.server.hostname.encode(),
)
struct.pack_into(
f"{len_hostname}s",
ret,
0x029C + offset,
self.core_config.title.hostname.encode(),
self.core_config.server.hostname.encode(),
)
struct.pack_into(
f"{len_hostname}s",
ret,
0x031C + offset,
self.core_config.title.hostname.encode(),
self.core_config.server.hostname.encode(),
)
struct.pack_into("<I", ret, 0x39C + offset, self.game_cfg.ports.echo)

View File

@@ -1,12 +1,12 @@
from twisted.web.http import Request
from starlette.requests import Request
from starlette.responses import Response, PlainTextResponse
from starlette.routing import Route
import yaml
import logging
import coloredlogs
from logging.handlers import TimedRotatingFileHandler
from os import path
from typing import Tuple, List, Dict
from twisted.internet import reactor, endpoints
from twisted.web import server, resource
import importlib
from core.config import CoreConfig
@@ -83,16 +83,15 @@ class IDZServlet(BaseServlet):
return True
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
return[
[("render_GET", "/idz/news/{endpoint:.*?}", {}),
("render_GET", "/idz/error", {})],
[]
def get_routes(self) -> List[Route]:
return [
Route("/idz/news/{endpoint:str}", self.render_GET),
Route("/idz/error", self.render_GET)
]
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
hostname = (
self.core_cfg.title.hostname
self.core_cfg.server.hostname
if not self.game_cfg.server.hostname
else self.game_cfg.server.hostname
)
@@ -135,7 +134,8 @@ class IDZServlet(BaseServlet):
except AttributeError as e:
continue
"""
endpoints.serverFromString(
reactor,
f"tcp:{self.game_cfg.ports.userdb}:interface={self.core_cfg.server.listen_address}",
@@ -155,18 +155,15 @@ class IDZServlet(BaseServlet):
reactor.listenUDP(
self.game_cfg.ports.userdb + 1, IDZEcho(self.core_cfg, self.game_cfg)
)
"""
self.logger.info(f"UserDB Listening on port {self.game_cfg.ports.userdb}")
def render_GET(self, request: Request, game_code: str, matchers: Dict) -> bytes:
url_path = matchers['endpoint']
async def render_GET(self, request: Request) -> bytes:
url_path = request.path_params.get('endpoint', '')
if not url_path:
return Response()
self.logger.info(f"IDZ GET request: {url_path}")
request.responseHeaders.setRawHeaders(
"Content-Type", [b"text/plain; charset=utf-8"]
)
request.responseHeaders.setRawHeaders(
"Last-Modified", [b"Sun, 23 Apr 2023 05:33:20 GMT"]
)
news = (
self.game_cfg.server.news
@@ -176,4 +173,4 @@ class IDZServlet(BaseServlet):
news += "\r\n"
news = "1979/01/01 00:00:00 2099/12/31 23:59:59 " + news
return news.encode()
return PlainTextResponse(news, media_type="text/plain; charset=utf-8", headers={"Last-Modified": "Sun, 23 Apr 2023 05:33:20 GMT"})

View File

@@ -5,7 +5,7 @@ import struct
from typing import Dict, Optional, List, Type
from twisted.web import server, resource
from twisted.internet import reactor, endpoints
from twisted.web.http import Request
from starlette.requests import Request
from routes import Mapper
import random
from os import walk

View File

@@ -26,12 +26,12 @@ class Mai2Base:
self.date_time_format = "%Y-%m-%d %H:%M:%S"
if not self.core_config.server.is_using_proxy and Utils.get_title_port(self.core_config) != 80:
self.old_server = f"http://{self.core_config.title.hostname}:{Utils.get_title_port(cfg)}/197/MaimaiServlet/"
self.old_server = f"http://{self.core_config.server.hostname}:{Utils.get_title_port(cfg)}/197/MaimaiServlet/"
else:
self.old_server = f"http://{self.core_config.title.hostname}/197/MaimaiServlet/"
self.old_server = f"http://{self.core_config.server.hostname}/197/MaimaiServlet/"
def handle_get_game_setting_api_request(self, data: Dict):
async def handle_get_game_setting_api_request(self, data: Dict):
# if reboot start/end time is not defined use the default behavior of being a few hours ago
if self.core_config.title.reboot_start_time == "" or self.core_config.title.reboot_end_time == "":
reboot_start = datetime.strftime(
@@ -74,14 +74,14 @@ class Mai2Base:
},
}
def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
return {"length": 0, "gameRankingList": []}
def handle_get_game_tournament_info_api_request(self, data: Dict) -> Dict:
async def handle_get_game_tournament_info_api_request(self, data: Dict) -> Dict:
# TODO: Tournament support
return {"length": 0, "gameTournamentInfoList": []}
def handle_get_game_event_api_request(self, data: Dict) -> Dict:
async def handle_get_game_event_api_request(self, data: Dict) -> Dict:
events = self.data.static.get_enabled_events(self.version)
events_lst = []
if events is None or not events:
@@ -108,10 +108,10 @@ class Mai2Base:
"gameEventList": events_lst,
}
def handle_get_game_ng_music_id_api_request(self, data: Dict) -> Dict:
async def handle_get_game_ng_music_id_api_request(self, data: Dict) -> Dict:
return {"length": 0, "musicIdList": []}
def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
async def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
game_charge_list = self.data.static.get_enabled_tickets(self.version, 1)
if game_charge_list is None:
return {"length": 0, "gameChargeList": []}
@@ -130,19 +130,19 @@ class Mai2Base:
return {"length": len(charge_list), "gameChargeList": charge_list}
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
def handle_upsert_client_upload_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_upload_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1, "apiName": "UpsertClientUploadApi"}
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1, "apiName": "UpsertClientBookkeepingApi"}
def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
async def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_detail(data["userId"], self.version, False)
w = self.data.profile.get_web_option(data["userId"], self.version)
if p is None or w is None:
@@ -169,7 +169,7 @@ class Mai2Base:
"totalLv": profile["totalLv"],
}
def handle_user_login_api_request(self, data: Dict) -> Dict:
async def handle_user_login_api_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
consec = self.data.profile.get_consec_login(data["userId"], self.version)
@@ -210,7 +210,7 @@ class Mai2Base:
"consecutiveLoginCount": consec_ct, # Number of consecutive days we've logged in.
}
def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict:
async def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
playlog = data["userPlaylog"]
@@ -218,7 +218,7 @@ class Mai2Base:
return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"}
def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
charge = data["userCharge"]
@@ -234,7 +234,7 @@ class Mai2Base:
return {"returnCode": 1, "apiName": "UpsertUserChargelogApi"}
def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
upsert = data["upsertUserAll"]
@@ -375,10 +375,10 @@ class Mai2Base:
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
def handle_user_logout_api_request(self, data: Dict) -> Dict:
async def handle_user_logout_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
async def handle_get_user_data_api_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_detail(data["userId"], self.version, False)
if profile is None:
return
@@ -390,7 +390,7 @@ class Mai2Base:
return {"userId": data["userId"], "userData": profile_dict}
def handle_get_user_extend_api_request(self, data: Dict) -> Dict:
async def handle_get_user_extend_api_request(self, data: Dict) -> Dict:
extend = self.data.profile.get_profile_extend(data["userId"], self.version)
if extend is None:
return
@@ -402,7 +402,7 @@ class Mai2Base:
return {"userId": data["userId"], "userExtend": extend_dict}
def handle_get_user_option_api_request(self, data: Dict) -> Dict:
async def handle_get_user_option_api_request(self, data: Dict) -> Dict:
options = self.data.profile.get_profile_option(data["userId"], self.version, False)
if options is None:
return
@@ -414,7 +414,7 @@ class Mai2Base:
return {"userId": data["userId"], "userOption": options_dict}
def handle_get_user_card_api_request(self, data: Dict) -> Dict:
async def handle_get_user_card_api_request(self, data: Dict) -> Dict:
user_cards = self.data.item.get_cards(data["userId"])
if user_cards is None:
return {"userId": data["userId"], "nextIndex": 0, "userCardList": []}
@@ -448,7 +448,7 @@ class Mai2Base:
"userCardList": card_list[start_idx:end_idx],
}
def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
async def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
user_charges = self.data.item.get_charges(data["userId"])
if user_charges is None:
return {"userId": data["userId"], "length": 0, "userChargeList": []}
@@ -467,16 +467,16 @@ class Mai2Base:
"userChargeList": user_charge_list,
}
def handle_get_user_present_api_request(self, data: Dict) -> Dict:
async def handle_get_user_present_api_request(self, data: Dict) -> Dict:
return { "userId": data.get("userId", 0), "length": 0, "userPresentList": []}
def handle_get_transfer_friend_api_request(self, data: Dict) -> Dict:
async def handle_get_transfer_friend_api_request(self, data: Dict) -> Dict:
return {}
def handle_get_user_present_event_api_request(self, data: Dict) -> Dict:
async def handle_get_user_present_event_api_request(self, data: Dict) -> Dict:
return { "userId": data.get("userId", 0), "length": 0, "userPresentEventList": []}
def handle_get_user_boss_api_request(self, data: Dict) -> Dict:
async def handle_get_user_boss_api_request(self, data: Dict) -> Dict:
b = self.data.profile.get_boss_list(data["userId"])
if b is None:
return { "userId": data.get("userId", 0), "userBossData": {}}
@@ -486,7 +486,7 @@ class Mai2Base:
return { "userId": data.get("userId", 0), "userBossData": boss_lst}
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
async def handle_get_user_item_api_request(self, data: Dict) -> Dict:
kind = int(data["nextIndex"] / 10000000000)
next_idx = int(data["nextIndex"] % 10000000000)
user_item_list = self.data.item.get_items(data["userId"], kind)
@@ -514,7 +514,7 @@ class Mai2Base:
"userItemList": items,
}
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
async def handle_get_user_character_api_request(self, data: Dict) -> Dict:
characters = self.data.item.get_characters(data["userId"])
chara_list = []
@@ -528,7 +528,7 @@ class Mai2Base:
return {"userId": data["userId"], "userCharacterList": chara_list}
def handle_get_user_favorite_api_request(self, data: Dict) -> Dict:
async def handle_get_user_favorite_api_request(self, data: Dict) -> Dict:
favorites = self.data.item.get_favorites(data["userId"], data["itemKind"])
if favorites is None:
return
@@ -545,7 +545,7 @@ class Mai2Base:
return {"userId": data["userId"], "userFavoriteData": userFavs}
def handle_get_user_ghost_api_request(self, data: Dict) -> Dict:
async def handle_get_user_ghost_api_request(self, data: Dict) -> Dict:
ghost = self.data.profile.get_profile_ghost(data["userId"], self.version)
if ghost is None:
return
@@ -557,7 +557,7 @@ class Mai2Base:
return {"userId": data["userId"], "userGhost": ghost_dict}
def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
async def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
rating = self.data.profile.get_recent_rating(data["userId"])
if rating is None:
return
@@ -567,7 +567,7 @@ class Mai2Base:
return {"userId": data["userId"], "length": len(lst), "userRecentRatingList": lst}
def handle_get_user_rating_api_request(self, data: Dict) -> Dict:
async def handle_get_user_rating_api_request(self, data: Dict) -> Dict:
rating = self.data.profile.get_profile_rating(data["userId"], self.version)
if rating is None:
return
@@ -579,7 +579,7 @@ class Mai2Base:
return {"userId": data["userId"], "userRating": rating_dict}
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
async def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
"""
kind 1 is playlist, kind 2 is music list
"""
@@ -607,7 +607,7 @@ class Mai2Base:
return {"userActivity": {"playList": plst, "musicList": mlst}}
def handle_get_user_course_api_request(self, data: Dict) -> Dict:
async def handle_get_user_course_api_request(self, data: Dict) -> Dict:
user_courses = self.data.score.get_courses(data["userId"])
if user_courses is None:
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []}
@@ -621,11 +621,11 @@ class Mai2Base:
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": course_list}
def handle_get_user_portrait_api_request(self, data: Dict) -> Dict:
async def handle_get_user_portrait_api_request(self, data: Dict) -> Dict:
# No support for custom pfps
return {"length": 0, "userPortraitList": []}
def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict:
async def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict:
friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"])
if friend_season_ranking is None:
return {
@@ -661,7 +661,7 @@ class Mai2Base:
"userFriendSeasonRankingList": friend_season_ranking_list,
}
def handle_get_user_map_api_request(self, data: Dict) -> Dict:
async def handle_get_user_map_api_request(self, data: Dict) -> Dict:
maps = self.data.item.get_maps(data["userId"])
if maps is None:
return {
@@ -694,7 +694,7 @@ class Mai2Base:
"userMapList": map_list,
}
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
login_bonuses = self.data.item.get_login_bonuses(data["userId"])
if login_bonuses is None:
return {
@@ -727,10 +727,10 @@ class Mai2Base:
"userLoginBonusList": login_bonus_list,
}
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
async def handle_get_user_region_api_request(self, data: Dict) -> Dict:
return {"userId": data["userId"], "length": 0, "userRegionList": []}
def handle_get_user_web_option_api_request(self, data: Dict) -> Dict:
async def handle_get_user_web_option_api_request(self, data: Dict) -> Dict:
w = self.data.profile.get_web_option(data["userId"], self.version)
if w is None:
return {"userId": data["userId"], "userWebOption": {}}
@@ -742,10 +742,10 @@ class Mai2Base:
return {"userId": data["userId"], "userWebOption": web_opt}
def handle_get_user_survival_api_request(self, data: Dict) -> Dict:
async def handle_get_user_survival_api_request(self, data: Dict) -> Dict:
return {"userId": data["userId"], "length": 0, "userSurvivalList": []}
def handle_get_user_grade_api_request(self, data: Dict) -> Dict:
async def handle_get_user_grade_api_request(self, data: Dict) -> Dict:
g = self.data.profile.get_grade_status(data["userId"])
if g is None:
return {"userId": data["userId"], "userGradeStatus": {}, "length": 0, "userGradeList": []}
@@ -755,7 +755,7 @@ class Mai2Base:
return {"userId": data["userId"], "userGradeStatus": grade_stat, "length": 0, "userGradeList": []}
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
async def handle_get_user_music_api_request(self, data: Dict) -> Dict:
user_id = data.get("userId", 0)
next_index = data.get("nextIndex", 0)
max_ct = data.get("maxCount", 50)
@@ -794,10 +794,10 @@ class Mai2Base:
"userMusicList": [{"userMusicDetailList": music_detail_list}],
}
def handle_upload_user_portrait_api_request(self, data: Dict) -> Dict:
async def handle_upload_user_portrait_api_request(self, data: Dict) -> Dict:
self.logger.debug(data)
def handle_upload_user_photo_api_request(self, data: Dict) -> Dict:
async def handle_upload_user_photo_api_request(self, data: Dict) -> Dict:
if not self.game_config.uploads.photos or not self.game_config.uploads.photos_dir:
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}

View File

@@ -15,7 +15,7 @@ class Mai2DX(Mai2Base):
super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX
def handle_get_game_setting_api_request(self, data: Dict):
async def handle_get_game_setting_api_request(self, data: Dict):
return {
"gameSetting": {
"isMaintenance": False,
@@ -33,7 +33,7 @@ class Mai2DX(Mai2Base):
"isAouAccession": False,
}
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
async def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_detail(data["userId"], self.version)
o = self.data.profile.get_profile_option(data["userId"], self.version)
if p is None or o is None:
@@ -69,7 +69,7 @@ class Mai2DX(Mai2Base):
else 0, # New with uni+
}
def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict:
async def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
playlog = data["userPlaylog"]
@@ -77,7 +77,7 @@ class Mai2DX(Mai2Base):
return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"}
def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
charge = data["userCharge"]
@@ -93,7 +93,7 @@ class Mai2DX(Mai2Base):
return {"returnCode": 1, "apiName": "UpsertUserChargelogApi"}
def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
upsert = data["upsertUserAll"]
@@ -215,7 +215,7 @@ class Mai2DX(Mai2Base):
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
async def handle_get_user_data_api_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
if profile is None:
return
@@ -227,7 +227,7 @@ class Mai2DX(Mai2Base):
return {"userId": data["userId"], "userData": profile_dict}
def handle_get_user_extend_api_request(self, data: Dict) -> Dict:
async def handle_get_user_extend_api_request(self, data: Dict) -> Dict:
extend = self.data.profile.get_profile_extend(data["userId"], self.version)
if extend is None:
return
@@ -239,7 +239,7 @@ class Mai2DX(Mai2Base):
return {"userId": data["userId"], "userExtend": extend_dict}
def handle_get_user_option_api_request(self, data: Dict) -> Dict:
async def handle_get_user_option_api_request(self, data: Dict) -> Dict:
options = self.data.profile.get_profile_option(data["userId"], self.version)
if options is None:
return
@@ -251,7 +251,7 @@ class Mai2DX(Mai2Base):
return {"userId": data["userId"], "userOption": options_dict}
def handle_get_user_card_api_request(self, data: Dict) -> Dict:
async def handle_get_user_card_api_request(self, data: Dict) -> Dict:
user_cards = self.data.item.get_cards(data["userId"])
if user_cards is None:
return {"userId": data["userId"], "nextIndex": 0, "userCardList": []}
@@ -285,7 +285,7 @@ class Mai2DX(Mai2Base):
"userCardList": card_list[start_idx:end_idx],
}
def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
async def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
user_charges = self.data.item.get_charges(data["userId"])
if user_charges is None:
return {"userId": data["userId"], "length": 0, "userChargeList": []}
@@ -310,7 +310,7 @@ class Mai2DX(Mai2Base):
"userChargeList": user_charge_list,
}
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
async def handle_get_user_item_api_request(self, data: Dict) -> Dict:
kind = int(data["nextIndex"] / 10000000000)
next_idx = int(data["nextIndex"] % 10000000000)
user_item_list = self.data.item.get_items(data["userId"], kind)
@@ -338,7 +338,7 @@ class Mai2DX(Mai2Base):
"userItemList": items,
}
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
async def handle_get_user_character_api_request(self, data: Dict) -> Dict:
characters = self.data.item.get_characters(data["userId"])
chara_list = []
@@ -350,7 +350,7 @@ class Mai2DX(Mai2Base):
return {"userId": data["userId"], "userCharacterList": chara_list}
def handle_get_user_favorite_api_request(self, data: Dict) -> Dict:
async def handle_get_user_favorite_api_request(self, data: Dict) -> Dict:
favorites = self.data.item.get_favorites(data["userId"], data["itemKind"])
if favorites is None:
return
@@ -367,7 +367,7 @@ class Mai2DX(Mai2Base):
return {"userId": data["userId"], "userFavoriteData": userFavs}
def handle_get_user_ghost_api_request(self, data: Dict) -> Dict:
async def handle_get_user_ghost_api_request(self, data: Dict) -> Dict:
ghost = self.data.profile.get_profile_ghost(data["userId"], self.version)
if ghost is None:
return
@@ -379,7 +379,7 @@ class Mai2DX(Mai2Base):
return {"userId": data["userId"], "userGhost": ghost_dict}
def handle_get_user_rating_api_request(self, data: Dict) -> Dict:
async def handle_get_user_rating_api_request(self, data: Dict) -> Dict:
rating = self.data.profile.get_profile_rating(data["userId"], self.version)
if rating is None:
return
@@ -391,7 +391,7 @@ class Mai2DX(Mai2Base):
return {"userId": data["userId"], "userRating": rating_dict}
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
async def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
"""
kind 1 is playlist, kind 2 is music list
"""
@@ -419,7 +419,7 @@ class Mai2DX(Mai2Base):
return {"userActivity": {"playList": plst, "musicList": mlst}}
def handle_get_user_course_api_request(self, data: Dict) -> Dict:
async def handle_get_user_course_api_request(self, data: Dict) -> Dict:
user_courses = self.data.score.get_courses(data["userId"])
if user_courses is None:
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []}
@@ -433,11 +433,11 @@ class Mai2DX(Mai2Base):
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": course_list}
def handle_get_user_portrait_api_request(self, data: Dict) -> Dict:
async def handle_get_user_portrait_api_request(self, data: Dict) -> Dict:
# No support for custom pfps
return {"length": 0, "userPortraitList": []}
def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict:
async def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict:
friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"])
if friend_season_ranking is None:
return {
@@ -473,7 +473,7 @@ class Mai2DX(Mai2Base):
"userFriendSeasonRankingList": friend_season_ranking_list,
}
def handle_get_user_map_api_request(self, data: Dict) -> Dict:
async def handle_get_user_map_api_request(self, data: Dict) -> Dict:
maps = self.data.item.get_maps(data["userId"])
if maps is None:
return {
@@ -506,7 +506,7 @@ class Mai2DX(Mai2Base):
"userMapList": map_list,
}
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
login_bonuses = self.data.item.get_login_bonuses(data["userId"])
if login_bonuses is None:
return {
@@ -539,7 +539,7 @@ class Mai2DX(Mai2Base):
"userLoginBonusList": login_bonus_list,
}
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
async def handle_get_user_region_api_request(self, data: Dict) -> Dict:
"""
class UserRegionList:
regionId: int
@@ -548,7 +548,7 @@ class Mai2DX(Mai2Base):
"""
return {"userId": data["userId"], "length": 0, "userRegionList": []}
def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
async def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
rival_id = data["rivalId"]
@@ -559,7 +559,7 @@ class Mai2DX(Mai2Base):
"""
return {"userId": user_id, "userRivalData": {}}
def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
async def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
rival_id = data["rivalId"]
next_idx = data["nextIndex"]
@@ -577,7 +577,7 @@ class Mai2DX(Mai2Base):
"""
return {"userId": user_id, "nextIndex": 0, "userRivalMusicList": []}
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
async def handle_get_user_music_api_request(self, data: Dict) -> Dict:
user_id = data.get("userId", 0)
next_index = data.get("nextIndex", 0)
max_ct = data.get("maxCount", 50)
@@ -616,8 +616,8 @@ class Mai2DX(Mai2Base):
"userMusicList": [{"userMusicDetailList": music_detail_list}],
}
def handle_user_login_api_request(self, data: Dict) -> Dict:
ret = super().handle_user_login_api_request(data)
async def handle_user_login_api_request(self, data: Dict) -> Dict:
ret = await super().handle_user_login_api_request(data)
if ret is None or not ret:
return ret
ret['loginId'] = ret.get('loginCount', 0)

View File

@@ -11,26 +11,26 @@ class Mai2Festival(Mai2UniversePlus):
super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = super().handle_cm_get_user_preview_api_request(data)
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = await super().handle_cm_get_user_preview_api_request(data)
# hardcode lastDataVersion for CardMaker
user_data["lastDataVersion"] = "1.30.00"
return user_data
def handle_user_login_api_request(self, data: Dict) -> Dict:
user_login = super().handle_user_login_api_request(data)
async def handle_user_login_api_request(self, data: Dict) -> Dict:
user_login = await super().handle_user_login_api_request(data)
# useless?
user_login["Bearer"] = "ARTEMiSTOKEN"
return user_login
def handle_get_user_recommend_rate_music_api_request(self, data: Dict) -> Dict:
async def handle_get_user_recommend_rate_music_api_request(self, data: Dict) -> Dict:
"""
userRecommendRateMusicIdList: list[int]
"""
return {"userId": data["userId"], "userRecommendRateMusicIdList": []}
def handle_get_user_recommend_select_music_api_request(self, data: Dict) -> Dict:
async def handle_get_user_recommend_select_music_api_request(self, data: Dict) -> Dict:
"""
userRecommendSelectionMusicIdList: list[int]
"""

View File

@@ -11,14 +11,14 @@ class Mai2FestivalPlus(Mai2Festival):
super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = super().handle_cm_get_user_preview_api_request(data)
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = await super().handle_cm_get_user_preview_api_request(data)
# hardcode lastDataVersion for CardMaker
user_data["lastDataVersion"] = "1.35.00"
return user_data
def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
async def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
user_id = data.get("userId", 0)
kind = data.get("kind", 2)
next_index = data.get("nextIndex", 0)

View File

@@ -1,9 +1,9 @@
from twisted.web.http import Request
from twisted.web.server import NOT_DONE_YET
from starlette.requests import Request
from starlette.responses import Response, JSONResponse
from starlette.routing import Route
import json
import inflection
import yaml
import string
import logging, coloredlogs
import zlib
from logging.handlers import TimedRotatingFileHandler
@@ -101,33 +101,29 @@ class Mai2Servlet(BaseServlet):
return True
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
return (
[
("handle_movie", "/{version}/MaimaiServlet/api/movie/{endpoint}", {}),
("handle_old_srv", "/{version}/MaimaiServlet/old/{endpoint}", {}),
("handle_old_srv_userdata", "/{version}/MaimaiServlet/old/{endpoint}/{placeid}/{keychip}/{userid}", {}),
("handle_old_srv_userdata", "/{version}/MaimaiServlet/old/{endpoint}/{userid}", {}),
("handle_usbdl", "/{version}/MaimaiServlet/usbdl/{endpoint}", {}),
("handle_deliver", "/{version}/MaimaiServlet/deliver/{endpoint}", {}),
],
[
("handle_movie", "/{version}/MaimaiServlet/api/movie/{endpoint}", {}),
("handle_mai", "/{version}/MaimaiServlet/{endpoint}", {}),
("handle_mai2", "/{version}/Maimai2Servlet/{endpoint}", {}),
]
)
def get_routes(self) -> List[Route]:
return [
Route("/{version:int}/MaimaiServlet/api/movie/{endpoint:str}", self.handle_movie, methods=['GET', 'POST']),
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}", self.handle_old_srv),
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{placeid:str}/{keychip:str}/{userid:int}", self.handle_old_srv_userdata),
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{userid:int}", self.handle_old_srv_userdata),
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{userid:int}", self.handle_old_srv_userdata),
Route("/{version:int}/MaimaiServlet/usbdl/{endpoint:str}", self.handle_usbdl),
Route("/{version:int}/MaimaiServlet/deliver/{endpoint:str}", self.handle_deliver),
Route("/{version:int}/MaimaiServlet/{endpoint:str}", self.handle_mai, methods=['POST']),
Route("/{version:int}/Maimai2Servlet/{endpoint:str}", self.handle_mai2, methods=['POST']),
]
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
return (
f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_ver}/",
f"{self.core_cfg.title.hostname}",
f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_ver}/",
f"{self.core_cfg.server.hostname}",
)
return (
f"http://{self.core_cfg.title.hostname}/{game_ver}/",
f"{self.core_cfg.title.hostname}",
f"http://{self.core_cfg.server.hostname}/{game_ver}/",
f"{self.core_cfg.server.hostname}",
)
def setup(self):
@@ -155,13 +151,22 @@ class Mai2Servlet(BaseServlet):
f"Failed to make movie upload directory at {self.game_cfg.uploads.movies_dir}"
)
def handle_mai(self, request: Request, game_code: str, matchers: Dict) -> bytes:
endpoint = matchers['endpoint']
version = int(matchers['version'])
async def handle_movie(self, request: Request):
return JSONResponse()
async def handle_usbdl(self, request: Request):
return Response("OK")
async def handle_deliver(self, request: Request):
return Response(status_code=404)
async def handle_mai(self, request: Request) -> bytes:
endpoint: str = request.path_params.get('endpoint')
version: int = request.path_params.get('version')
if endpoint.lower() == "ping":
return zlib.compress(b'{"returnCode": "1"}')
return Response(zlib.compress(b'{"returnCode": "1"}'))
req_raw = request.content.getvalue()
req_raw = await request.body()
internal_ver = 0
client_ip = Utils.get_ip_addr(request)
@@ -199,7 +204,7 @@ class Mai2Servlet(BaseServlet):
self.logger.error(
f"Failed to decompress v{version} {endpoint} request -> {e}"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
req_data = json.loads(unzip)
@@ -216,26 +221,26 @@ class Mai2Servlet(BaseServlet):
else:
try:
handler = getattr(handler_cls, func_to_find)
resp = handler(req_data)
resp = await handler(req_data)
except Exception as e:
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
return zlib.compress(b'{"returnCode": "0"}')
return Response(zlib.compress(b'{"returnCode": "0"}'))
if resp == None:
resp = {"returnCode": 1}
self.logger.debug(f"Response {resp}")
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
return Response(zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")))
def handle_mai2(self, request: Request, game_code: str, matchers: Dict) -> bytes:
endpoint = matchers['endpoint']
version = int(matchers['version'])
async def handle_mai2(self, request: Request) -> bytes:
endpoint: str = request.path_params.get('endpoint')
version: int = request.path_params.get('version')
if endpoint.lower() == "ping":
return zlib.compress(b'{"returnCode": "1"}')
return Response(zlib.compress(b'{"returnCode": "1"}'))
req_raw = request.content.getvalue()
req_raw = await request.body()
internal_ver = 0
client_ip = Utils.get_ip_addr(request)
if version < 105: # 1.0
@@ -256,17 +261,17 @@ class Mai2Servlet(BaseServlet):
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
if (
request.getHeader("Mai-Encoding") is not None
or request.getHeader("X-Mai-Encoding") is not None
request.headers.get("Mai-Encoding") is not None
or request.headers.get("X-Mai-Encoding") is not None
):
# The has is some flavor of MD5 of the endpoint with a constant bolted onto the end of it.
# See cake.dll's Obfuscator function for details. Hopefully most DLL edits will remove
# these two(?) headers to not cause issues, but given the general quality of SEGA data...
enc_ver = request.getHeader("Mai-Encoding")
enc_ver = request.headers.get("Mai-Encoding")
if enc_ver is None:
enc_ver = request.getHeader("X-Mai-Encoding")
enc_ver = request.headers.get("X-Mai-Encoding")
self.logger.debug(
f"Encryption v{enc_ver} - User-Agent: {request.getHeader('User-Agent')}"
f"Encryption v{enc_ver} - User-Agent: {request.headers.get('User-Agent')}"
)
try:
@@ -276,7 +281,7 @@ class Mai2Servlet(BaseServlet):
self.logger.error(
f"Failed to decompress v{version} {endpoint} request -> {e}"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
req_data = json.loads(unzip)
@@ -293,80 +298,27 @@ class Mai2Servlet(BaseServlet):
else:
try:
handler = getattr(handler_cls, func_to_find)
resp = handler(req_data)
resp = await handler(req_data)
except Exception as e:
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
if resp == None:
resp = {"returnCode": 1}
self.logger.debug(f"Response {resp}")
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
return Response(zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")))
def handle_old_srv(self, request: Request, game_code: str, matchers: Dict) -> bytes:
endpoint = matchers['endpoint']
version = matchers['version']
async def handle_old_srv(self, request: Request) -> bytes:
endpoint = request.path_params.get('endpoint')
version = request.path_params.get('version')
self.logger.info(f"v{version} old server {endpoint}")
return zlib.compress(b"ok")
return Response(zlib.compress(b"ok"))
def handle_old_srv_userdata(self, request: Request, game_code: str, matchers: Dict) -> bytes:
endpoint = matchers['endpoint']
version = matchers['version']
async def handle_old_srv_userdata(self, request: Request) -> bytes:
endpoint = request.path_params.get('endpoint')
version = request.path_params.get('version')
self.logger.info(f"v{version} old server {endpoint}")
return zlib.compress(b"{}")
def render_GET(self, request: Request, version: int, url_path: str) -> bytes:
self.logger.debug(f"v{version} GET {url_path}")
url_split = url_path.split("/")
if (url_split[0] == "api" and url_split[1] == "movie") or url_split[
0
] == "movie":
if url_split[2] == "moviestart":
return json.dumps({"moviestart": {"status": "OK"}}).encode()
else:
request.setResponseCode(404)
return b""
elif url_split[0] == "usbdl":
if url_split[1] == "CONNECTIONTEST":
self.logger.info(f"v{version} usbdl server test")
return b""
elif self.game_cfg.deliver.udbdl_enable and path.exists(
f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}"
):
with open(
f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}", "rb"
) as f:
return f.read()
else:
request.setResponseCode(404)
return b""
elif url_split[0] == "deliver":
file = url_split[len(url_split) - 1]
self.logger.info(f"v{version} {file} deliver inquire")
self.logger.debug(
f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}"
)
if self.game_cfg.deliver.enable and path.exists(
f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}"
):
with open(
f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}", "rb"
) as f:
return f.read()
else:
request.setResponseCode(404)
return b""
else:
return zlib.compress(b"{}")
return Response(zlib.compress(b"{}"))

View File

@@ -15,7 +15,7 @@ class Mai2Universe(Mai2SplashPlus):
super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_detail(data["userId"], self.version)
if p is None:
return {}
@@ -30,7 +30,7 @@ class Mai2Universe(Mai2SplashPlus):
"isExistSellingCard": True,
}
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
# user already exists, because the preview checks that already
p = self.data.profile.get_profile_detail(data["userId"], self.version)
@@ -52,13 +52,13 @@ class Mai2Universe(Mai2SplashPlus):
return {"userId": data["userId"], "userData": user_data}
def handle_cm_login_api_request(self, data: Dict) -> Dict:
async def handle_cm_login_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}
def handle_cm_logout_api_request(self, data: Dict) -> Dict:
async def handle_cm_logout_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}
def handle_cm_get_selling_card_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_selling_card_api_request(self, data: Dict) -> Dict:
selling_cards = self.data.static.get_enabled_cards(self.version)
if selling_cards is None:
return {"length": 0, "sellingCardList": []}
@@ -88,7 +88,7 @@ class Mai2Universe(Mai2SplashPlus):
return {"length": len(selling_card_list), "sellingCardList": selling_card_list}
def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict:
user_cards = self.data.item.get_cards(data["userId"])
if user_cards is None:
return {"returnCode": 1, "length": 0, "nextIndex": 0, "userCardList": []}
@@ -124,10 +124,10 @@ class Mai2Universe(Mai2SplashPlus):
"userCardList": card_list[start_idx:end_idx],
}
def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
super().handle_get_user_item_api_request(data)
def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
characters = self.data.item.get_characters(data["userId"])
chara_list = []
@@ -153,10 +153,10 @@ class Mai2Universe(Mai2SplashPlus):
"userCharacterList": chara_list,
}
def handle_cm_get_user_card_print_error_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_card_print_error_api_request(self, data: Dict) -> Dict:
return {"length": 0, "userPrintDetailList": []}
def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
upsert = data["userPrintDetail"]
@@ -209,12 +209,12 @@ class Mai2Universe(Mai2SplashPlus):
"endDate": datetime.strftime(end_date, Mai2Constants.DATE_TIME_FORMAT),
}
def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
return {
"returnCode": 1,
"orderId": 0,
"serialId": data["userPrintlog"]["serialId"],
}
def handle_cm_upsert_buy_card_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_buy_card_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1}

View File

@@ -11,8 +11,8 @@ class Mai2UniversePlus(Mai2Universe):
super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = super().handle_cm_get_user_preview_api_request(data)
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = await super().handle_cm_get_user_preview_api_request(data)
# hardcode lastDataVersion for CardMaker 1.35
user_data["lastDataVersion"] = "1.25.00"

View File

@@ -103,7 +103,7 @@ class OngekiBase:
self.game = OngekiConstants.GAME_CODE
self.version = OngekiConstants.VER_ONGEKI
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
# if reboot start/end time is not defined use the default behavior of being a few hours ago
if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
reboot_start = datetime.strftime(
@@ -148,7 +148,7 @@ class OngekiBase:
"isAou": "true",
}
def handle_get_game_idlist_api_request(self, data: Dict) -> Dict:
async 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?
"""
@@ -156,7 +156,7 @@ class OngekiBase:
# id - int
return {"type": data["type"], "length": 0, "gameIdlistList": []}
def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
game_ranking_list = self.data.static.get_ranking_list(self.version)
ranking_list = []
@@ -171,7 +171,7 @@ class OngekiBase:
"gameRankingList": ranking_list,
}
def handle_get_game_point_api_request(self, data: Dict) -> Dict:
async def handle_get_game_point_api_request(self, data: Dict) -> Dict:
get_game_point = self.data.static.get_static_game_point()
game_point = []
@@ -194,16 +194,16 @@ class OngekiBase:
"gamePointList": game_point,
}
def handle_game_login_api_request(self, data: Dict) -> Dict:
async 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:
async 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:
async 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:
async def handle_get_game_reward_api_request(self, data: Dict) -> Dict:
get_game_rewards = self.data.static.get_reward_list(self.version)
reward_list = []
@@ -221,7 +221,7 @@ class OngekiBase:
"gameRewardList": reward_list,
}
def handle_get_game_present_api_request(self, data: Dict) -> Dict:
async def handle_get_game_present_api_request(self, data: Dict) -> Dict:
get_present = self.data.static.get_present_list(self.version)
present_list = []
@@ -238,13 +238,13 @@ class OngekiBase:
"gamePresentList": present_list,
}
def handle_get_game_message_api_request(self, data: Dict) -> Dict:
async 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:
async 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:
async def handle_get_game_tech_music_api_request(self, data: Dict) -> Dict:
music_list = self.data.static.get_tech_music(self.version)
prep_music_list = []
@@ -262,7 +262,7 @@ class OngekiBase:
"gameTechMusicList": prep_music_list,
}
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
if self.core_cfg.server.is_develop:
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
@@ -273,7 +273,7 @@ class OngekiBase:
self.data.static.put_client_setting_data(cab['id'], client_setting_data)
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
async def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
if self.core_cfg.server.is_develop:
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
@@ -282,16 +282,16 @@ class OngekiBase:
self.data.static.put_client_testmode_data(region_id, client_testmode_data)
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
async 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:
async 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:
async 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:
async def handle_upsert_user_gplog_api_request(self, data: Dict) -> Dict:
user = data["userId"]
if user >= 200000000000000: # Account for guest play
user = None
@@ -309,10 +309,10 @@ class OngekiBase:
return {"returnCode": 1, "apiName": "UpsertUserGplogApi"}
def handle_extend_lock_time_api_request(self, data: Dict) -> Dict:
async 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:
async def handle_get_game_event_api_request(self, data: Dict) -> Dict:
evts = self.data.static.get_enabled_events(self.version)
if evts is None:
@@ -342,7 +342,7 @@ class OngekiBase:
"gameEventList": evt_list,
}
def handle_get_game_id_list_api_request(self, data: Dict) -> Dict:
async 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:
@@ -362,10 +362,10 @@ class OngekiBase:
"gameIdlistList": game_idlist,
}
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
async 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:
async 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:
@@ -417,7 +417,7 @@ class OngekiBase:
"isWarningConfirmed": True,
}
def handle_get_user_tech_count_api_request(self, data: Dict) -> Dict:
async 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
@@ -436,7 +436,7 @@ class OngekiBase:
"userTechCountList": userTechCountList,
}
def handle_get_user_tech_event_api_request(self, data: Dict) -> Dict:
async def handle_get_user_tech_event_api_request(self, data: Dict) -> Dict:
user_tech_event_list = self.data.item.get_tech_event(self.version, data["userId"])
if user_tech_event_list is None:
return {}
@@ -455,7 +455,7 @@ class OngekiBase:
"userTechEventList": tech_evt,
}
def handle_get_user_tech_event_ranking_api_request(self, data: Dict) -> Dict:
async def handle_get_user_tech_event_ranking_api_request(self, data: Dict) -> Dict:
user_tech_event_ranks = self.data.item.get_tech_event_ranking(self.version, data["userId"])
if user_tech_event_ranks is None:
return {
@@ -481,7 +481,7 @@ class OngekiBase:
"userTechEventRankingList": evt_ranking,
}
def handle_get_user_kop_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -496,7 +496,7 @@ class OngekiBase:
"userKopList": kop_list,
}
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
async 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"]
@@ -516,7 +516,7 @@ class OngekiBase:
"userMusicList": song_list[start_idx:end_idx],
}
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
async def handle_get_user_item_api_request(self, data: Dict) -> Dict:
kind = data["nextIndex"] / 10000000000
p = self.data.item.get_items(data["userId"], kind)
@@ -552,7 +552,7 @@ class OngekiBase:
"userItemList": items,
}
def handle_get_user_option_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -566,7 +566,7 @@ class OngekiBase:
return {"userId": data["userId"], "userOption": user_opts}
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -594,7 +594,7 @@ class OngekiBase:
return {"userId": data["userId"], "userData": user_data}
def handle_get_user_event_ranking_api_request(self, data: Dict) -> Dict:
async def handle_get_user_event_ranking_api_request(self, data: Dict) -> Dict:
user_event_ranking_list = self.data.item.get_ranking_event_ranks(self.version, data["userId"])
if user_event_ranking_list is None:
return {}
@@ -617,7 +617,7 @@ class OngekiBase:
"userEventRankingList": prep_event_ranking,
}
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -635,7 +635,7 @@ class OngekiBase:
"userLoginBonusList": login_bonuses,
}
def handle_get_user_bp_base_request(self, data: Dict) -> Dict:
async 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"]
)
@@ -648,7 +648,7 @@ class OngekiBase:
"userBpBaseList": profile["userBpBaseList"],
}
def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
async 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 {
@@ -665,7 +665,7 @@ class OngekiBase:
"userRecentRatingList": userRecentRatingList,
}
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -692,7 +692,7 @@ class OngekiBase:
"userActivityList": user_activity,
}
def handle_get_user_story_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -710,7 +710,7 @@ class OngekiBase:
"userStoryList": story_list,
}
def handle_get_user_chapter_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -728,14 +728,14 @@ class OngekiBase:
"userChapterList": chapter_list,
}
def handle_get_user_training_room_by_key_api_request(self, data: Dict) -> Dict:
async 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:
async 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 {}
@@ -753,7 +753,7 @@ class OngekiBase:
"userCharacterList": character_list,
}
def handle_get_user_card_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -771,7 +771,7 @@ class OngekiBase:
"userCardList": card_list,
}
def handle_get_user_deck_by_key_api_request(self, data: Dict) -> Dict:
async 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:
@@ -790,7 +790,7 @@ class OngekiBase:
"userDeckList": deck_list,
}
def handle_get_user_trade_item_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -808,7 +808,7 @@ class OngekiBase:
"userTradeItemList": trade_item_list,
}
def handle_get_user_scenario_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -826,7 +826,7 @@ class OngekiBase:
"userScenarioList": scenerio_list,
}
def handle_get_user_ratinglog_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -844,7 +844,7 @@ class OngekiBase:
"userRatinglogList": userRatinglogList,
}
def handle_get_user_mission_point_api_request(self, data: Dict) -> Dict:
async def handle_get_user_mission_point_api_request(self, data: Dict) -> Dict:
user_mission_point_list = self.data.item.get_mission_points(self.version, data["userId"])
if user_mission_point_list is None:
return {}
@@ -864,7 +864,7 @@ class OngekiBase:
"userMissionPointList": mission_point_list,
}
def handle_get_user_event_point_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -886,7 +886,7 @@ class OngekiBase:
"userEventPointList": event_point_list,
}
def handle_get_user_music_item_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -904,7 +904,7 @@ class OngekiBase:
"userMusicItemList": music_item_list,
}
def handle_get_user_event_music_api_request(self, data: Dict) -> Dict:
async 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 {}
@@ -922,7 +922,7 @@ class OngekiBase:
"userEventMusicList": evt_music_list,
}
def handle_get_user_boss_api_request(self, data: Dict) -> Dict:
async def handle_get_user_boss_api_request(self, data: Dict) -> Dict:
p = self.data.item.get_bosses(data["userId"])
if p is None:
return {}
@@ -940,7 +940,7 @@ class OngekiBase:
"userBossList": boss_list,
}
def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
upsert = data["upsertUserAll"]
user_id = data["userId"]
@@ -1070,7 +1070,7 @@ class OngekiBase:
return {"returnCode": 1, "apiName": "upsertUserAll"}
def handle_get_user_rival_api_request(self, data: Dict) -> Dict:
async def handle_get_user_rival_api_request(self, data: Dict) -> Dict:
"""
Added in Bright
"""
@@ -1094,7 +1094,7 @@ class OngekiBase:
"userRivalList": rival_list,
}
def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
async def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
"""
Added in Bright
"""
@@ -1112,7 +1112,7 @@ class OngekiBase:
"userRivalDataList": rivals,
}
def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
async def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
"""
Added in Bright
"""

View File

@@ -15,13 +15,13 @@ class OngekiBright(OngekiBase):
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)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.30.00"
ret["gameSetting"]["onlineDataVersion"] = "1.30.00"
return ret
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
# check for a bright profile
p = self.data.profile.get_profile_data(data["userId"], self.version)
if p is None:
@@ -55,13 +55,13 @@ class OngekiBright(OngekiBase):
return {"userId": data["userId"], "userData": user_data}
def handle_printer_login_api_request(self, data: Dict):
async def handle_printer_login_api_request(self, data: Dict):
return {"returnCode": 1}
def handle_printer_logout_api_request(self, data: Dict):
async def handle_printer_logout_api_request(self, data: Dict):
return {"returnCode": 1}
def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict:
user_cards = self.data.item.get_cards(data["userId"])
if user_cards is None:
return {}
@@ -90,7 +90,7 @@ class OngekiBright(OngekiBase):
"userCardList": card_list[start_idx:end_idx],
}
def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
user_characters = self.data.item.get_characters(data["userId"])
if user_characters is None:
return {
@@ -124,7 +124,7 @@ class OngekiBright(OngekiBase):
"userCharacterList": character_list[start_idx:end_idx],
}
def handle_get_user_gacha_api_request(self, data: Dict) -> Dict:
async def handle_get_user_gacha_api_request(self, data: Dict) -> Dict:
user_gachas = self.data.item.get_user_gachas(data["userId"])
if user_gachas is None:
return {"userId": data["userId"], "length": 0, "userGachaList": []}
@@ -143,10 +143,10 @@ class OngekiBright(OngekiBase):
"userGachaList": user_gacha_list,
}
def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
return self.handle_get_user_item_api_request(data)
def handle_cm_get_user_gacha_supply_api_request(self, data: Dict) -> Dict:
async def handle_cm_get_user_gacha_supply_api_request(self, data: Dict) -> Dict:
# not used for now? not sure what it even does
user_gacha_supplies = self.data.item.get_user_gacha_supplies(data["userId"])
if user_gacha_supplies is None:
@@ -160,7 +160,7 @@ class OngekiBright(OngekiBase):
"supplyCardList": supply_list,
}
def handle_get_game_gacha_api_request(self, data: Dict) -> Dict:
async def handle_get_game_gacha_api_request(self, data: Dict) -> Dict:
"""
returns all current active banners (gachas)
"Select Gacha" requires maxSelectPoint set and isCeiling set to 1
@@ -207,7 +207,7 @@ class OngekiBright(OngekiBase):
"registIdList": [],
}
def handle_roll_gacha_api_request(self, data: Dict) -> Dict:
async def handle_roll_gacha_api_request(self, data: Dict) -> Dict:
"""
Handle a gacha roll API request
"""
@@ -323,7 +323,7 @@ class OngekiBright(OngekiBase):
"gameGachaCardList": game_gacha_card_list,
}
def handle_cm_upsert_user_gacha_api_request(self, data: Dict):
async def handle_cm_upsert_user_gacha_api_request(self, data: Dict):
upsert = data["cmUpsertUserGacha"]
user_id = data["userId"]
@@ -405,7 +405,7 @@ class OngekiBright(OngekiBase):
return {"returnCode": 1, "apiName": "CMUpsertUserGachaApi"}
def handle_cm_upsert_user_select_gacha_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_select_gacha_api_request(self, data: Dict) -> Dict:
upsert = data["cmUpsertUserSelectGacha"]
user_id = data["userId"]
@@ -442,7 +442,7 @@ class OngekiBright(OngekiBase):
return {"returnCode": 1, "apiName": "cmUpsertUserSelectGacha"}
def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict:
async def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict:
game_gacha_cards = self.data.static.get_gacha_cards(data["gachaId"])
if game_gacha_cards == []:
# fallback to be at least able to select that gacha
@@ -522,7 +522,7 @@ class OngekiBright(OngekiBase):
"ssrBookCalcList": [],
}
def handle_get_game_theater_api_request(self, data: Dict) -> Dict:
async def handle_get_game_theater_api_request(self, data: Dict) -> Dict:
"""
shows a banner after every print, not sure what its used for
"""
@@ -548,7 +548,7 @@ class OngekiBright(OngekiBase):
return {"length": 0, "gameTheaterList": [], "registIdList": []}
def handle_cm_upsert_user_print_playlog_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_print_playlog_api_request(self, data: Dict) -> Dict:
return {
"returnCode": 1,
"orderId": 0,
@@ -556,7 +556,7 @@ class OngekiBright(OngekiBase):
"apiName": "CMUpsertUserPrintPlaylogApi",
}
def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
return {
"returnCode": 1,
"orderId": 0,
@@ -564,7 +564,7 @@ class OngekiBright(OngekiBase):
"apiName": "CMUpsertUserPrintlogApi",
}
def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
user_print_detail = data["userPrintDetail"]
# generate random serial id
@@ -589,7 +589,7 @@ class OngekiBright(OngekiBase):
"apiName": "CMUpsertUserPrintApi",
}
def handle_cm_upsert_user_all_api_request(self, data: Dict) -> Dict:
async def handle_cm_upsert_user_all_api_request(self, data: Dict) -> Dict:
upsert = data["cmUpsertUserAll"]
user_id = data["userId"]

View File

@@ -15,8 +15,8 @@ class OngekiBrightMemory(OngekiBright):
super().__init__(core_cfg, game_cfg)
self.version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.35.00"
ret["gameSetting"]["onlineDataVersion"] = "1.35.00"
ret["gameSetting"]["maxCountCharacter"] = 50
@@ -27,7 +27,7 @@ class OngekiBrightMemory(OngekiBright):
ret["gameSetting"]["maxCountRivalMusic"] = 300
return ret
def handle_get_user_memory_chapter_api_request(self, data: Dict) -> Dict:
async def handle_get_user_memory_chapter_api_request(self, data: Dict) -> Dict:
memories = self.data.item.get_memorychapters(data["userId"])
if not memories:
return {
@@ -134,5 +134,5 @@ class OngekiBrightMemory(OngekiBright):
"userMemoryChapterList": memory_chp,
}
def handle_get_game_music_release_state_api_request(self, data: Dict) -> Dict:
async def handle_get_game_music_release_state_api_request(self, data: Dict) -> Dict:
return {"techScore": 0, "cardNum": 0}

View File

@@ -1,6 +1,6 @@
import yaml
import jinja2
from twisted.web.http import Request
from starlette.requests import Request
from os import path
from twisted.web.util import redirectTo
from twisted.web.server import Session

View File

@@ -1,4 +1,6 @@
from twisted.web.http import Request
from starlette.requests import Request
from starlette.routing import Route
from starlette.responses import Response
import json
import inflection
import yaml
@@ -120,35 +122,28 @@ class OngekiServlet(BaseServlet):
return True
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
return (
[],
[("render_POST", "/SDDT/{version}/{endpoint}", {})]
)
def get_routes(self) -> List[Route]:
return [
Route("/SDDT/{version:int}/{endpoint:str}", self.render_POST, methods=['POST'])
]
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
title_port_int = Utils.get_title_port(self.core_cfg)
title_port_ssl_int = Utils.get_title_port_ssl(self.core_cfg)
proto = "https" if self.game_cfg.server.use_https and game_ver >= 120 else "http"
if proto == "https":
t_port = f":{title_port_ssl_int}" if title_port_ssl_int and not self.core_cfg.server.is_using_proxy else ""
else:
t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else ""
t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else ""
return (
f"{proto}://{self.core_cfg.title.hostname}{t_port}/{game_code}/{game_ver}/",
f"{self.core_cfg.title.hostname}{t_port}/",
f"{proto}://{self.core_cfg.server.hostname}{t_port}/{game_code}/{game_ver}/",
f"{self.core_cfg.server.hostname}{t_port}/",
)
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
endpoint = matchers['endpoint']
version = int(matchers['version'])
async def render_POST(self, request: Request) -> bytes:
endpoint: str = request.path_params.get('endpoint', '')
version: int = request.path_params.get('version', 0)
if endpoint.lower() == "ping":
return zlib.compress(b'{"returnCode": 1}')
return Response(zlib.compress(b'{"returnCode": 1}'))
req_raw = request.content.getvalue()
req_raw = await request.body()
encrtped = False
internal_ver = 0
client_ip = Utils.get_ip_addr(request)
@@ -178,13 +173,13 @@ class OngekiServlet(BaseServlet):
self.logger.error(
f"v{version} does not support encryption or no keys entered"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
elif endpoint.lower() not in self.hash_table[internal_ver]:
self.logger.error(
f"No hash found for v{version} endpoint {endpoint}"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
endpoint = self.hash_table[internal_ver][endpoint.lower()]
@@ -201,7 +196,7 @@ class OngekiServlet(BaseServlet):
self.logger.error(
f"Failed to decrypt v{version} request to {endpoint} -> {e}"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
encrtped = True
@@ -213,7 +208,7 @@ class OngekiServlet(BaseServlet):
self.logger.error(
f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
try:
unzip = zlib.decompress(req_raw)
@@ -222,7 +217,7 @@ class OngekiServlet(BaseServlet):
self.logger.error(
f"Failed to decompress v{version} {endpoint} request -> {e}"
)
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
req_data = json.loads(unzip)
@@ -235,15 +230,15 @@ class OngekiServlet(BaseServlet):
if not hasattr(self.versions[internal_ver], func_to_find):
self.logger.warning(f"Unhandled v{version} request {endpoint}")
return zlib.compress(b'{"returnCode": 1}')
return Response(zlib.compress(b'{"returnCode": 1}'))
try:
handler = getattr(self.versions[internal_ver], func_to_find)
resp = handler(req_data)
resp = await handler(req_data)
except Exception as e:
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
return zlib.compress(b'{"stat": "0"}')
return Response(zlib.compress(b'{"stat": "0"}'))
if resp == None:
resp = {"returnCode": 1}
@@ -253,7 +248,7 @@ class OngekiServlet(BaseServlet):
zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
if not encrtped or version < 120:
return zipped
return Response(zipped)
padded = pad(zipped, 16)
@@ -263,4 +258,4 @@ class OngekiServlet(BaseServlet):
bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]),
)
return crypt.encrypt(padded)
return Response(crypt.encrypt(padded))

View File

@@ -11,8 +11,8 @@ class OngekiPlus(OngekiBase):
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)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.05.00"
ret["gameSetting"]["onlineDataVersion"] = "1.05.00"
return ret

View File

@@ -11,8 +11,8 @@ class OngekiRed(OngekiBase):
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)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.20.00"
ret["gameSetting"]["onlineDataVersion"] = "1.20.00"
return ret

View File

@@ -11,8 +11,8 @@ class OngekiRedPlus(OngekiBase):
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)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.25.00"
ret["gameSetting"]["onlineDataVersion"] = "1.25.00"
ret["gameSetting"]["maxCountCharacter"] = 50

View File

@@ -11,8 +11,8 @@ class OngekiSummer(OngekiBase):
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)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.10.00"
ret["gameSetting"]["onlineDataVersion"] = "1.10.00"
return ret

View File

@@ -11,8 +11,8 @@ class OngekiSummerPlus(OngekiBase):
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)
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = await super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.15.00"
ret["gameSetting"]["onlineDataVersion"] = "1.15.00"
return ret

View File

@@ -20,21 +20,21 @@ class PokkenBase:
self.data = PokkenData(core_cfg)
self.SUPPORT_SET_NONE = 4294967295
def handle_noop(self, request: Any) -> bytes:
async def handle_noop(self, request: Any) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = request.type
return res.SerializeToString()
def handle_ping(self, request: jackal_pb2.Request) -> bytes:
async def handle_ping(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.PING
return res.SerializeToString()
def handle_register_pcb(self, request: jackal_pb2.Request) -> bytes:
async def handle_register_pcb(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.REGISTER_PCB
@@ -61,35 +61,35 @@ class PokkenBase:
"logfilename": "JackalMatchingLibrary.log",
"biwalogfilename": "./biwa.log",
}
regist_pcb.bnp_baseuri = f"{self.core_cfg.title.hostname}/bna"
regist_pcb.bnp_baseuri = f"{self.core_cfg.server.hostname}/bna"
regist_pcb.biwa_setting = json.dumps(biwa_setting)
res.register_pcb.CopyFrom(regist_pcb)
return res.SerializeToString()
def handle_save_ads(self, request: jackal_pb2.Request) -> bytes:
async def handle_save_ads(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.SAVE_ADS
return res.SerializeToString()
def handle_save_client_log(self, request: jackal_pb2.Request) -> bytes:
async def handle_save_client_log(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.SAVE_CLIENT_LOG
return res.SerializeToString()
def handle_check_diagnosis(self, request: jackal_pb2.Request) -> bytes:
async def handle_check_diagnosis(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.CHECK_DIAGNOSIS
return res.SerializeToString()
def handle_load_client_settings(self, request: jackal_pb2.Request) -> bytes:
async def handle_load_client_settings(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.LOAD_CLIENT_SETTINGS
@@ -112,7 +112,7 @@ class PokkenBase:
return res.SerializeToString()
def handle_load_ranking(self, request: jackal_pb2.Request) -> bytes:
async def handle_load_ranking(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.LOAD_RANKING
@@ -126,7 +126,7 @@ class PokkenBase:
res.load_ranking.CopyFrom(ranking)
return res.SerializeToString()
def handle_load_user(self, request: jackal_pb2.Request) -> bytes:
async def handle_load_user(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.LOAD_USER
@@ -287,13 +287,13 @@ class PokkenBase:
res.load_user.CopyFrom(load_usr)
return res.SerializeToString()
def handle_set_bnpassid_lock(self, data: jackal_pb2.Request) -> bytes:
async def handle_set_bnpassid_lock(self, data: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.SET_BNPASSID_LOCK
return res.SerializeToString()
def handle_save_user(self, request: jackal_pb2.Request) -> bytes:
async def handle_save_user(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.SAVE_USER
@@ -394,38 +394,31 @@ class PokkenBase:
return res.SerializeToString()
def handle_save_ingame_log(self, data: jackal_pb2.Request) -> bytes:
async def handle_save_ingame_log(self, data: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.SAVE_INGAME_LOG
return res.SerializeToString()
def handle_save_charge(self, data: jackal_pb2.Request) -> bytes:
async def handle_save_charge(self, data: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.SAVE_CHARGE
return res.SerializeToString()
def handle_matching_noop(
async def handle_matching_noop(
self, data: Dict = {}, client_ip: str = "127.0.0.1"
) -> Dict:
return {}
def handle_matching_start_matching(
async def handle_matching_start_matching(
self, data: Dict = {}, client_ip: str = "127.0.0.1"
) -> Dict:
return {}
def handle_matching_is_matching(
async def handle_matching_is_matching(
self, data: Dict = {}, client_ip: str = "127.0.0.1"
) -> Dict:
"""
"sessionId":"12345678",
"A":{
"pcb_id": data["data"]["must"]["pcb_id"],
"gip": client_ip
},
"""
return {
"data": {
"sessionId":"12345678",
@@ -437,15 +430,15 @@ class PokkenBase:
}
}
def handle_matching_stop_matching(
async def handle_matching_stop_matching(
self, data: Dict = {}, client_ip: str = "127.0.0.1"
) -> Dict:
return {}
def handle_admission_noop(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict:
async def handle_admission_noop(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict:
return {}
def handle_admission_joinsession(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict:
async def handle_admission_joinsession(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict:
self.logger.info(f"Admission: JoinSession from {req_ip}")
return {
'data': {

View File

@@ -3,6 +3,7 @@ from enum import Enum
class PokkenConstants:
GAME_CODE = "SDAK"
GAME_CDS = ["PKF1"]
CONFIG_NAME = "pokken.yaml"
@@ -10,6 +11,12 @@ class PokkenConstants:
VERSION_NAMES = "Pokken Tournament"
SERIAL_IDENT = [2747]
NETID_PREFIX = ["ABGN"]
SERIAL_REGIONS = [1]
SERIAL_ROLES = [3]
SERIAL_CAB_IDENTS = [19]
class BATTLE_TYPE(Enum):
TUTORIAL = 1
AI = 2

View File

@@ -1,6 +1,6 @@
import yaml
import jinja2
from twisted.web.http import Request
from starlette.requests import Request
from os import path
from twisted.web.server import Session

View File

@@ -1,8 +1,10 @@
from typing import Tuple, List, Dict
from twisted.web.http import Request
from twisted.web import resource
from twisted.internet import reactor
import json, ast
from starlette.requests import Request
from starlette.requests import Request
from starlette.responses import Response, JSONResponse
from starlette.routing import Route, WebSocketRoute
from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect
import ast
from datetime import datetime
import yaml
import logging, coloredlogs
@@ -17,8 +19,6 @@ from .config import PokkenConfig
from .base import PokkenBase
from .const import PokkenConstants
from .proto import jackal_pb2
from .services import PokkenAdmissionFactory
class PokkenServlet(BaseServlet):
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
@@ -69,47 +69,73 @@ class PokkenServlet(BaseServlet):
return True
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
return (
[],
[
("render_POST", "/pokken/", {}),
("handle_matching", "/pokken/matching", {}),
]
)
def get_routes(self) -> List[Route]:
return [
Route("/pokken/", self.render_POST, methods=['POST']),
Route("/pokken/matching", self.handle_matching, methods=['POST']),
WebSocketRoute("/pokken/admission", self.handle_admission)
]
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
if self.game_cfg.ports.game != 443:
return (
f"https://{self.game_cfg.server.hostname}:{self.game_cfg.ports.game}/pokken/",
f"{self.game_cfg.server.hostname}/pokken/",
)
return (
f"https://{self.game_cfg.server.hostname}/pokken/",
f"{self.game_cfg.server.hostname}/pokken/",
f"https://{self.game_cfg.server.hostname}:{self.game_cfg.ports.game}/pokken/",
f"{self.game_cfg.server.hostname}:{self.game_cfg.ports.game}/pokken/",
)
def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]:
game_cfg = PokkenConfig()
if not self.game_cfg.server.enable:
return (False, [], [])
if path.exists(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}"):
game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}"))
)
return (True, PokkenConstants.GAME_CDS, PokkenConstants.NETID_PREFIX)
async def handle_admission(self, ws: WebSocket) -> None:
client_ip = Utils.get_ip_addr(ws)
await ws.accept()
while True:
try:
msg: Dict = await ws.receive_json()
except WebSocketDisconnect as e:
self.logger.debug(f"Client {client_ip} disconnected - {e}")
break
except Exception as e:
self.logger.error(f"Could not load JSON from message from {client_ip} - {e}")
if ws.client_state != WebSocketState.DISCONNECTED:
await ws.close()
break
self.logger.debug(f"Admission: Message from {client_ip}:{ws.client.port} - {msg}")
api = msg.get("api", "noop")
handler = getattr(self.base, f"handle_admission_{api.lower()}")
resp = await handler(msg, client_ip)
if resp is None:
resp = {}
if not game_cfg.server.enable:
return (False, "")
if "type" not in resp:
resp['type'] = "res"
if "data" not in resp:
resp['data'] = {}
if "api" not in resp:
resp['api'] = api
if "result" not in resp:
resp['result'] = 'true'
self.logger.debug(f"Websocket response: {resp}")
try:
await ws.send_json(resp)
except WebSocketDisconnect as e:
self.logger.debug(f"Client {client_ip} disconnected - {e}")
break
except Exception as e:
self.logger.error(f"Could not send JSON message to {client_ip} - {e}")
break
if ws.client_state != WebSocketState.DISCONNECTED:
await ws.close()
return (True, "PKF1")
def setup(self) -> None:
if self.game_cfg.server.enable_matching:
reactor.listenTCP(
self.game_cfg.ports.admission, PokkenAdmissionFactory(self.core_cfg, self.game_cfg)
)
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
content = request.content.getvalue()
async def render_POST(self, request: Request) -> bytes:
content = await request.body()
if content == b"":
self.logger.info("Empty request")
return b""
@@ -134,19 +160,19 @@ class PokkenServlet(BaseServlet):
self.logger.info(f"{endpoint} request from {Utils.get_ip_addr(request)}")
ret = handler(pokken_request)
return ret
ret = await handler(pokken_request)
return Response(ret)
def handle_matching(self, request: Request, game_code: str, matchers: Dict) -> bytes:
async def handle_matching(self, request: Request) -> bytes:
if not self.game_cfg.server.enable_matching:
return b""
return Response()
content = request.content.getvalue()
content = await request.body()
client_ip = Utils.get_ip_addr(request)
if content is None or content == b"":
self.logger.info("Empty matching request")
return json.dumps(self.base.handle_matching_noop()).encode()
return JSONResponse(self.base.handle_matching_noop())
json_content = ast.literal_eval(
content.decode()
@@ -166,7 +192,7 @@ class PokkenServlet(BaseServlet):
self.logger.warning(
f"No handler found for message type {json_content['call']}"
)
return json.dumps(self.base.handle_matching_noop()).encode()
return JSONResponse(self.base.handle_matching_noop())
ret = handler(json_content, client_ip)
@@ -181,4 +207,4 @@ class PokkenServlet(BaseServlet):
self.logger.debug(f"Response {ret}")
return json.dumps(ret).encode()
return JSONResponse(ret)

View File

@@ -32,42 +32,42 @@ class SaoBase:
self.logger.warning(f"Failed to find csv file {file}.csv")
return ret
def handle_noop(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_noop(self, header: SaoRequestHeader, request: bytes) -> bytes:
self.logger.info(f"Using Generic handler")
resp_thing = SaoNoopResponse(header.cmd + 1)
return resp_thing.make()
def handle_c122(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c122(self, header: SaoRequestHeader, request: bytes) -> bytes:
#common/get_maintenance_info
resp = SaoGetMaintResponse(header.cmd +1)
return resp.make()
def handle_c12e(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c12e(self, header: SaoRequestHeader, request: bytes) -> bytes:
#common/ac_cabinet_boot_notification
resp = SaoCommonAcCabinetBootNotificationResponse(header.cmd +1)
return resp.make()
def handle_c100(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c100(self, header: SaoRequestHeader, request: bytes) -> bytes:
#common/get_app_versions
resp = SaoCommonGetAppVersionsRequest(header.cmd +1)
return resp.make()
def handle_c102(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c102(self, header: SaoRequestHeader, request: bytes) -> bytes:
#common/master_data_version_check
resp = SaoMasterDataVersionCheckResponse(header.cmd +1)
return resp.make()
def handle_c10a(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c10a(self, header: SaoRequestHeader, request: bytes) -> bytes:
#common/paying_play_start
resp = SaoCommonPayingPlayStartRequest(header.cmd +1)
return resp.make()
def handle_ca02(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_ca02(self, header: SaoRequestHeader, request: bytes) -> bytes:
#quest_multi_play_room/get_quest_scene_multi_play_photon_server
resp = SaoGetQuestSceneMultiPlayPhotonServerResponse(header.cmd +1)
return resp.make()
def handle_c11e(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c11e(self, header: SaoRequestHeader, request: bytes) -> bytes:
#common/get_auth_card_data
req = SaoGetAuthCardDataRequest(header, request)
@@ -120,12 +120,12 @@ class SaoBase:
resp = SaoGetAuthCardDataResponse(header.cmd +1, profile_data)
return resp.make()
def handle_c40c(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c40c(self, header: SaoRequestHeader, request: bytes) -> bytes:
#home/check_ac_login_bonus
resp = SaoHomeCheckAcLoginBonusResponse(header.cmd +1)
return resp.make()
def handle_c104(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c104(self, header: SaoRequestHeader, request: bytes) -> bytes:
#common/login
req = SaoCommonLoginRequest(header, request)
@@ -135,17 +135,17 @@ class SaoBase:
resp = SaoCommonLoginResponse(header.cmd +1, profile_data)
return resp.make()
def handle_c404(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c404(self, header: SaoRequestHeader, request: bytes) -> bytes:
#home/check_comeback_event
resp = SaoCheckComebackEventRequest(header.cmd +1)
return resp.make()
def handle_c000(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c000(self, header: SaoRequestHeader, request: bytes) -> bytes:
#ticket/ticket
resp = SaoTicketResponse(header.cmd +1)
return resp.make()
def handle_c500(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c500(self, header: SaoRequestHeader, request: bytes) -> bytes:
#user_info/get_user_basic_data
req = SaoGetUserBasicDataRequest(header, request)
@@ -154,7 +154,7 @@ class SaoBase:
resp = SaoGetUserBasicDataResponse(header.cmd +1, profile_data)
return resp.make()
def handle_c600(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c600(self, header: SaoRequestHeader, request: bytes) -> bytes:
#have_object/get_hero_log_user_data_list
req = SaoGetHeroLogUserDataListRequest(header, request)
@@ -163,7 +163,7 @@ class SaoBase:
resp = SaoGetHeroLogUserDataListResponse(header.cmd +1, hero_data)
return resp.make()
def handle_c602(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c602(self, header: SaoRequestHeader, request: bytes) -> bytes:
#have_object/get_equipment_user_data_list
req = SaoGetEquipmentUserDataListRequest(header, request)
@@ -172,7 +172,7 @@ class SaoBase:
resp = SaoGetEquipmentUserDataListResponse(header.cmd +1, equipment_data)
return resp.make()
def handle_c604(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c604(self, header: SaoRequestHeader, request: bytes) -> bytes:
#have_object/get_item_user_data_list
req = SaoGetItemUserDataListRequest(header, request)
@@ -181,21 +181,21 @@ class SaoBase:
resp = SaoGetItemUserDataListResponse(header.cmd +1, item_data)
return resp.make()
def handle_c606(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c606(self, header: SaoRequestHeader, request: bytes) -> bytes:
#have_object/get_support_log_user_data_list
supportIdsData = self.game_data.static.get_support_log_ids(0, True)
resp = SaoGetSupportLogUserDataListResponse(header.cmd +1, supportIdsData)
return resp.make()
def handle_c800(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c800(self, header: SaoRequestHeader, request: bytes) -> bytes:
#custom/get_title_user_data_list
titleIdsData = self.game_data.static.get_title_ids(0, True)
resp = SaoGetTitleUserDataListResponse(header.cmd +1, titleIdsData)
return resp.make()
def handle_c608(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c608(self, header: SaoRequestHeader, request: bytes) -> bytes:
#have_object/get_episode_append_data_list
req = SaoGetEpisodeAppendDataListRequest(header, request)
@@ -204,7 +204,7 @@ class SaoBase:
resp = SaoGetEpisodeAppendDataListResponse(header.cmd +1, profile_data)
return resp.make()
def handle_c804(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c804(self, header: SaoRequestHeader, request: bytes) -> bytes:
#custom/get_party_data_list
req = SaoGetPartyDataListRequest(header, request)
@@ -216,17 +216,17 @@ class SaoBase:
resp = SaoGetPartyDataListResponse(header.cmd +1, hero1_data, hero2_data, hero3_data)
return resp.make()
def handle_c902(self, header: SaoRequestHeader, request: bytes) -> bytes: # for whatever reason, having all entries empty or filled changes nothing
async def handle_c902(self, header: SaoRequestHeader, request: bytes) -> bytes: # for whatever reason, having all entries empty or filled changes nothing
#quest/get_quest_scene_prev_scan_profile_card
resp = SaoGetQuestScenePrevScanProfileCardResponse(header.cmd +1)
return resp.make()
def handle_c124(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c124(self, header: SaoRequestHeader, request: bytes) -> bytes:
#common/get_resource_path_info
resp = SaoGetResourcePathInfoResponse(header.cmd +1)
return resp.make()
def handle_c900(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c900(self, header: SaoRequestHeader, request: bytes) -> bytes:
#quest/get_quest_scene_user_data_list // QuestScene.csv
req = SaoGetQuestSceneUserDataListRequest(header, request)
@@ -235,22 +235,22 @@ class SaoBase:
resp = SaoGetQuestSceneUserDataListResponse(header.cmd +1, quest_data)
return resp.make()
def handle_c400(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c400(self, header: SaoRequestHeader, request: bytes) -> bytes:
#home/check_yui_medal_get_condition
resp = SaoCheckYuiMedalGetConditionResponse(header.cmd +1)
return resp.make()
def handle_c402(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c402(self, header: SaoRequestHeader, request: bytes) -> bytes:
#home/get_yui_medal_bonus_user_data
resp = SaoGetYuiMedalBonusUserDataResponse(header.cmd +1)
return resp.make()
def handle_c40a(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c40a(self, header: SaoRequestHeader, request: bytes) -> bytes:
#home/check_profile_card_used_reward
resp = SaoCheckProfileCardUsedRewardResponse(header.cmd +1)
return resp.make()
def handle_c814(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c814(self, header: SaoRequestHeader, request: bytes) -> bytes:
#custom/synthesize_enhancement_hero_log
req = SaoSynthesizeEnhancementHeroLogRequest(header, request)
@@ -324,7 +324,7 @@ class SaoBase:
resp = SaoSynthesizeEnhancementHeroLogResponse(header.cmd +1, synthesize_hero_log_data)
return resp.make()
def handle_c816(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c816(self, header: SaoRequestHeader, request: bytes) -> bytes:
#custom/synthesize_enhancement_equipment
req_data = SaoSynthesizeEnhancementEquipmentRequest(header, request)
synthesize_equipment_data = self.game_data.item.get_user_equipment(req_data.user_id, req_data.origin_user_equipment_id)
@@ -386,7 +386,7 @@ class SaoBase:
resp = SaoSynthesizeEnhancementEquipmentResponse(header.cmd +1, synthesize_equipment_data)
return resp.make()
def handle_c806(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c806(self, header: SaoRequestHeader, request: bytes) -> bytes:
#custom/change_party
req_data = SaoChangePartyRequest(header, request)
party_hero_list = []
@@ -421,7 +421,7 @@ class SaoBase:
resp = SaoNoopResponse(header.cmd +1)
return resp.make()
def handle_c904(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c904(self, header: SaoRequestHeader, request: bytes) -> bytes:
#quest/episode_play_start
req_data = SaoEpisodePlayStartRequest(header, request)
@@ -439,7 +439,7 @@ class SaoBase:
resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data)
return resp.make()
def handle_c908(self, header: SaoRequestHeader, request: bytes) -> bytes: # Level calculation missing for the profile and heroes
async def handle_c908(self, header: SaoRequestHeader, request: bytes) -> bytes: # Level calculation missing for the profile and heroes
#quest/episode_play_end
req_data = SaoEpisodePlayEndRequest(header, request)
@@ -599,7 +599,7 @@ class SaoBase:
resp = SaoEpisodePlayEndResponse(header.cmd +1)
return resp.make()
def handle_c914(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c914(self, header: SaoRequestHeader, request: bytes) -> bytes:
#quest/trial_tower_play_start
req_data = SaoTrialTowerPlayStartRequest(header, request)
@@ -618,7 +618,7 @@ class SaoBase:
resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data)
return resp.make()
def handle_c918(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c918(self, header: SaoRequestHeader, request: bytes) -> bytes:
#quest/trial_tower_play_end
req_data = SaoTrialTowerPlayEndRequest(header, request)
@@ -799,7 +799,7 @@ class SaoBase:
resp = SaoTrialTowerPlayEndResponse(header.cmd +1)
return resp.make()
def handle_c90a(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c90a(self, header: SaoRequestHeader, request: bytes) -> bytes:
#quest/episode_play_end_unanalyzed_log_fixed
req = SaoEpisodePlayEndUnanalyzedLogFixedRequest(header, request)
@@ -809,7 +809,7 @@ class SaoBase:
resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4])
return resp.make()
def handle_c91a(self, header: SaoRequestHeader, request: bytes) -> bytes: # handler is identical to the episode
async def handle_c91a(self, header: SaoRequestHeader, request: bytes) -> bytes: # handler is identical to the episode
#quest/trial_tower_play_end_unanalyzed_log_fixed
req = TrialTowerPlayEndUnanalyzedLogFixed(header, request)
@@ -818,58 +818,58 @@ class SaoBase:
resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4])
return resp.make()
def handle_cd00(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_cd00(self, header: SaoRequestHeader, request: bytes) -> bytes:
#defrag_match/get_defrag_match_basic_data
resp = SaoGetDefragMatchBasicDataResponse(header.cmd +1)
return resp.make()
def handle_cd02(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_cd02(self, header: SaoRequestHeader, request: bytes) -> bytes:
#defrag_match/get_defrag_match_ranking_user_data
resp = SaoGetDefragMatchRankingUserDataResponse(header.cmd +1)
return resp.make()
def handle_cd04(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_cd04(self, header: SaoRequestHeader, request: bytes) -> bytes:
#defrag_match/get_defrag_match_league_point_ranking_list
resp = SaoGetDefragMatchLeaguePointRankingListResponse(header.cmd +1)
return resp.make()
def handle_cd06(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_cd06(self, header: SaoRequestHeader, request: bytes) -> bytes:
#defrag_match/get_defrag_match_league_score_ranking_list
resp = SaoGetDefragMatchLeagueScoreRankingListResponse(header.cmd +1)
return resp.make()
def handle_d404(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_d404(self, header: SaoRequestHeader, request: bytes) -> bytes:
#other/bnid_serial_code_check
resp = SaoBnidSerialCodeCheckResponse(header.cmd +1)
return resp.make()
def handle_c306(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c306(self, header: SaoRequestHeader, request: bytes) -> bytes:
#card/scan_qr_quest_profile_card
resp = SaoScanQrQuestProfileCardResponse(header.cmd +1)
return resp.make()
def handle_c700(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_c700(self, header: SaoRequestHeader, request: bytes) -> bytes:
# shop/get_shop_resource_sales_data_list
# TODO: Get user shop data
req = GetShopResourceSalesDataListRequest(header, request)
resp = GetShopResourceSalesDataListResponse(header.cmd + 1)
return resp.make()
def handle_d100(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_d100(self, header: SaoRequestHeader, request: bytes) -> bytes:
# shop/get_yui_medal_shop_user_data_list
# TODO: Get user shop data
req = GetYuiMedalShopUserDataListRequest(header, request)
resp = GetYuiMedalShopUserDataListResponse(header.cmd + 1)
return resp.make()
def handle_cf0e(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_cf0e(self, header: SaoRequestHeader, request: bytes) -> bytes:
# gasha/get_gasha_medal_shop_user_data_list
# TODO: Get user shop data
req = GetGashaMedalShopUserDataListRequest(header, request)
resp = GetGashaMedalShopUserDataListResponse(header.cmd + 1)
return resp.make()
def handle_d5da(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_d5da(self, header: SaoRequestHeader, request: bytes) -> bytes:
# master_data/get_m_yui_medal_shops
req = GetMYuiMedalShopDataRequest(header, request)
resp = GetMYuiMedalShopDataResponse(header.cmd + 1)
@@ -897,7 +897,7 @@ class SaoBase:
self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shops")
return resp.make()
def handle_d5dc(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_d5dc(self, header: SaoRequestHeader, request: bytes) -> bytes:
# master_data/get_m_yui_medal_shop_items
req = GetMYuiMedalShopItemsRequest(header, request)
resp = GetMYuiMedalShopItemsResponse(header.cmd + 1)
@@ -927,7 +927,7 @@ class SaoBase:
self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shop Items")
return resp.make()
def handle_d5fc(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_d5fc(self, header: SaoRequestHeader, request: bytes) -> bytes:
# master_data/get_m_gasha_medal_shops
req = GetMGashaMedalShopsRequest(header, request)
resp = GetMGashaMedalShopsResponse(header.cmd + 1)
@@ -942,11 +942,11 @@ class SaoBase:
self.logger.debug(f"Load {len(resp.data_list)} Gasha Medal Shops")
return resp.make()
def handle_d5fe(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_d5fe(self, header: SaoRequestHeader, request: bytes) -> bytes:
# master_data/get_m_gasha_medal_shop_items
return SaoNoopResponse(header.cmd + 1).make()
def handle_d604(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_d604(self, header: SaoRequestHeader, request: bytes) -> bytes:
# master_data_2/get_m_res_earn_campaign_shops
req = GetMResEarnCampaignShopsRequest(header, request)
resp = GetMResEarnCampaignShopsResponse(header.cmd + 1)
@@ -968,6 +968,6 @@ class SaoBase:
#self.logger.debug(f"Load {len(resp.data_list)} Res Earn Campaign Shops")
return SaoNoopResponse(header.cmd + 1).make()
def handle_d606(self, header: SaoRequestHeader, request: bytes) -> bytes:
async def handle_d606(self, header: SaoRequestHeader, request: bytes) -> bytes:
# master_data_2/get_m_res_earn_campaign_shop_items
return SaoNoopResponse(header.cmd + 1).make()

View File

@@ -3,12 +3,22 @@ from enum import Enum
class SaoConstants:
GAME_CODE = "SDEW"
GAME_CDS = ["SAO1"]
CONFIG_NAME = "sao.yaml"
VER_SAO = 0
VERSION_NAMES = ("Sword Art Online Arcade")
SERIAL_IDENT_SATALITE = 4
SERIAL_IDENT_TERMINAL = 5
SERIAL_IDENT = [2825]
NETID_PREFIX = ["ABLN"]
SERIAL_REGIONS = [1]
SERIAL_ROLES = [3]
SERIAL_CAB_IDENTS = [SERIAL_IDENT_SATALITE, SERIAL_IDENT_TERMINAL]
@classmethod
def game_ver_to_string(cls, ver: int):

View File

@@ -1,5 +1,7 @@
from typing import Tuple, Dict, List
from twisted.web.http import Request
from starlette.requests import Request
from starlette.responses import Response
from starlette.routing import Route
import yaml
import logging, coloredlogs
from logging.handlers import TimedRotatingFileHandler
@@ -55,11 +57,10 @@ class SaoServlet(BaseServlet):
if self.game_cfg.hash.verify_hash:
self.static_hash = md5(self.game_cfg.hash.hash_base.encode()).digest() # Greate hashing guys, really validates the data
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
return (
[],
[("render_POST", "/{datecode}/proto/if/{category}/{endpoint}", {})]
)
def get_routes(self) -> List[Route]:
return [
Route("/{datecode:int}/proto/if/{category:str}/{endpoint:str}", self.render_POST, methods=['POST'])
]
@classmethod
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
@@ -86,30 +87,29 @@ class SaoServlet(BaseServlet):
proto = "https"
port = f":{port_ssl}" if not self.core_cfg.server.is_using_proxy and port_ssl != 443 else ""
return (f"{proto}://{self.core_cfg.title.hostname}{port}/", "")
return (f"{proto}://{self.core_cfg.server.hostname}{port}/", "")
def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]:
if not self.game_cfg.server.enable:
return (False, "")
return (False, [], [])
return (True, "SAO1")
return (True, SaoConstants.GAME_CDS, SaoConstants.NETID_PREFIX)
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
endpoint = matchers.get('endpoint', '')
request.responseHeaders.addRawHeader(b"content-type", b"text/html; charset=utf-8")
async def render_POST(self, request: Request) -> bytes:
endpoint = request.path_params.get('endpoint', '')
iv = b""
req_raw = request.content.read()
req_raw = await request.body()
if len(req_raw) < 40:
self.logger.warn(f"Malformed request to {endpoint} - {req_raw.hex()}")
return b""
return Response()
req_header = SaoRequestHeader(req_raw)
cmd_str = f"{req_header.cmd:04x}"
if self.game_cfg.hash.verify_hash and self.static_hash != req_header.hash:
self.logger.error(f"Hash mismatch! Expecting {self.static_hash} but recieved {req_header.hash}")
return b""
return Response()
if self.game_cfg.crypt.enable:
iv = req_raw[40:48]
@@ -124,7 +124,7 @@ class SaoServlet(BaseServlet):
handler = getattr(self.base, f"handle_{cmd_str}", self.base.handle_noop)
self.logger.info(f"{endpoint} - {cmd_str} request")
self.logger.debug(f"Request: {req_raw.hex()}")
resp = handler(req_header, req_data)
resp = await handler(req_header, req_data)
if resp is None:
resp = SaoNoopResponse(req_header.cmd + 1).make()
@@ -137,7 +137,7 @@ class SaoServlet(BaseServlet):
else:
self.logger.error(f"Unknown response type {type(resp)}")
return b""
return Response()
self.logger.debug(f"Response: {resp.hex()}")
@@ -153,5 +153,6 @@ class SaoServlet(BaseServlet):
tmp = struct.pack("!I", crypt_data_len) # does it want the length of the encrypted response??
resp = resp[:20] + tmp + iv + data_crypt
self.logger.debug(f"Encrypted Response: {resp.hex()}")
return resp
return Response(resp, media_type="text/html; charset=utf-8")

View File

@@ -83,18 +83,18 @@ class WaccaBase:
else:
self.region_id = WaccaConstants.Region[prefecture_name]
def handle_housing_get_request(self, data: Dict) -> Dict:
async def handle_housing_get_request(self, data: Dict) -> Dict:
req = BaseRequest(data)
housing_id = 1337
self.logger.info(f"{req.chipId} -> {housing_id}")
resp = HousingGetResponse(housing_id)
return resp.make()
def handle_advertise_GetRanking_request(self, data: Dict) -> Dict:
async def handle_advertise_GetRanking_request(self, data: Dict) -> Dict:
req = AdvertiseGetRankingRequest(data)
return AdvertiseGetRankingResponse().make()
def handle_housing_start_request(self, data: Dict) -> Dict:
async def handle_housing_start_request(self, data: Dict) -> Dict:
req = HousingStartRequestV1(data)
allnet_region_id = None
@@ -126,16 +126,16 @@ class WaccaBase:
resp = HousingStartResponseV1(region_id)
return resp.make()
def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
async def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
resp = GetNewsResponseV1()
return resp.make()
def handle_user_status_logout_request(self, data: Dict) -> Dict:
async def handle_user_status_logout_request(self, data: Dict) -> Dict:
req = UserStatusLogoutRequest(data)
self.logger.info(f"Log out user {req.userId} from {req.chipId}")
return BaseResponse().make()
def handle_user_status_get_request(self, data: Dict) -> Dict:
async def handle_user_status_get_request(self, data: Dict) -> Dict:
req = UserStatusGetRequest(data)
resp = UserStatusGetV1Response()
@@ -181,7 +181,7 @@ class WaccaBase:
return resp.make()
def handle_user_status_login_request(self, data: Dict) -> Dict:
async def handle_user_status_login_request(self, data: Dict) -> Dict:
req = UserStatusLoginRequest(data)
resp = UserStatusLoginResponseV1()
is_consec_day = True
@@ -227,7 +227,7 @@ class WaccaBase:
return resp.make()
def handle_user_status_create_request(self, data: Dict) -> Dict:
async def handle_user_status_create_request(self, data: Dict) -> Dict:
req = UserStatusCreateRequest(data)
profileId = self.data.profile.create_profile(
@@ -268,7 +268,7 @@ class WaccaBase:
return UserStatusCreateResponseV2(profileId, req.username).make()
def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
async def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
req = UserStatusGetDetailRequest(data)
resp = UserStatusGetDetailResponseV1()
@@ -433,7 +433,7 @@ class WaccaBase:
return resp.make()
def handle_user_trial_get_request(self, data: Dict) -> Dict:
async def handle_user_trial_get_request(self, data: Dict) -> Dict:
req = UserTrialGetRequest(data)
resp = UserTrialGetResponse()
@@ -475,7 +475,7 @@ class WaccaBase:
return resp.make()
def handle_user_trial_update_request(self, data: Dict) -> Dict:
async def handle_user_trial_update_request(self, data: Dict) -> Dict:
req = UserTrialUpdateRequest(data)
total_score = 0
@@ -571,7 +571,7 @@ class WaccaBase:
)
return BaseResponse().make()
def handle_user_sugoroku_update_request(self, data: Dict) -> Dict:
async def handle_user_sugoroku_update_request(self, data: Dict) -> Dict:
ver_split = data["appVersion"].split(".")
resp = BaseResponse()
@@ -603,10 +603,10 @@ class WaccaBase:
)
return resp.make()
def handle_user_info_getMyroom_request(self, data: Dict) -> Dict:
async def handle_user_info_getMyroom_request(self, data: Dict) -> Dict:
return UserInfogetMyroomResponseV1().make()
def handle_user_music_unlock_request(self, data: Dict) -> Dict:
async def handle_user_music_unlock_request(self, data: Dict) -> Dict:
req = UserMusicUnlockRequest(data)
profile = self.data.profile.get_profile(req.profileId)
@@ -672,12 +672,12 @@ class WaccaBase:
return UserMusicUnlockResponse(current_wp, new_tickets).make()
def handle_user_info_getRanking_request(self, data: Dict) -> Dict:
async def handle_user_info_getRanking_request(self, data: Dict) -> Dict:
# total score, high score by song, cumulative socre, stage up score, other score, WP ranking
# This likely requies calculating standings at regular intervals and caching the results
return UserInfogetRankingResponse().make()
def handle_user_music_update_request(self, data: Dict) -> Dict:
async def handle_user_music_update_request(self, data: Dict) -> Dict:
ver_split = data["appVersion"].split(".")
if int(ver_split[0]) >= 3:
resp = UserMusicUpdateResponseV3()
@@ -831,18 +831,18 @@ class WaccaBase:
return resp.make()
# TODO: Coop and vs data
def handle_user_music_updateCoop_request(self, data: Dict) -> Dict:
async def handle_user_music_updateCoop_request(self, data: Dict) -> Dict:
coop_info = data["params"][4]
return self.handle_user_music_update_request(data)
def handle_user_music_updateVersus_request(self, data: Dict) -> Dict:
async def handle_user_music_updateVersus_request(self, data: Dict) -> Dict:
vs_info = data["params"][4]
return self.handle_user_music_update_request(data)
def handle_user_music_updateTrial_request(self, data: Dict) -> Dict:
async def handle_user_music_updateTrial_request(self, data: Dict) -> Dict:
return self.handle_user_music_update_request(data)
def handle_user_mission_update_request(self, data: Dict) -> Dict:
async def handle_user_mission_update_request(self, data: Dict) -> Dict:
req = UserMissionUpdateRequest(data)
page_status = req.params[1][1]
@@ -860,7 +860,7 @@ class WaccaBase:
return BaseResponse().make()
def handle_user_goods_purchase_request(self, data: Dict) -> Dict:
async def handle_user_goods_purchase_request(self, data: Dict) -> Dict:
req = UserGoodsPurchaseRequest(data)
resp = UserGoodsPurchaseResponse()
@@ -905,13 +905,13 @@ class WaccaBase:
return resp.make()
def handle_competition_status_login_request(self, data: Dict) -> Dict:
async def handle_competition_status_login_request(self, data: Dict) -> Dict:
return BaseResponse().make()
def handle_competition_status_update_request(self, data: Dict) -> Dict:
async def handle_competition_status_update_request(self, data: Dict) -> Dict:
return BaseResponse().make()
def handle_user_rating_update_request(self, data: Dict) -> Dict:
async def handle_user_rating_update_request(self, data: Dict) -> Dict:
req = UserRatingUpdateRequest(data)
user_id = self.data.profile.profile_to_aime_user(req.profileId)
@@ -931,7 +931,7 @@ class WaccaBase:
return BaseResponse().make()
def handle_user_status_update_request(self, data: Dict) -> Dict:
async def handle_user_status_update_request(self, data: Dict) -> Dict:
req = UserStatusUpdateRequestV1(data)
user_id = self.data.profile.profile_to_aime_user(req.profileId)
@@ -970,7 +970,7 @@ class WaccaBase:
)
return BaseResponse().make()
def handle_user_info_update_request(self, data: Dict) -> Dict:
async def handle_user_info_update_request(self, data: Dict) -> Dict:
req = UserInfoUpdateRequest(data)
user_id = self.data.profile.profile_to_aime_user(req.profileId)
@@ -989,7 +989,7 @@ class WaccaBase:
return BaseResponse().make()
def handle_user_vip_get_request(self, data: Dict) -> Dict:
async def handle_user_vip_get_request(self, data: Dict) -> Dict:
req = UserVipGetRequest(data)
resp = UserVipGetResponse()
@@ -1021,7 +1021,7 @@ class WaccaBase:
return resp.make()
def handle_user_vip_start_request(self, data: Dict) -> Dict:
async def handle_user_vip_start_request(self, data: Dict) -> Dict:
req = UserVipStartRequest(data)
profile = self.data.profile.get_profile(req.profileId)

View File

@@ -1,6 +1,6 @@
import yaml
import jinja2
from twisted.web.http import Request
from starlette.requests import Request
from os import path
from twisted.web.server import Session

View File

@@ -1,16 +1,19 @@
from starlette.routing import Route
import yaml
import logging, coloredlogs
from logging.handlers import TimedRotatingFileHandler
import logging
import json
from hashlib import md5
from twisted.web.http import Request
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
from typing import Dict, Tuple, List
from os import path
import traceback
import sys
from core import CoreConfig, Utils
from core.title import BaseServlet
from .config import WaccaConfig
from .config import WaccaConfig
from .const import WaccaConstants
@@ -23,7 +26,7 @@ from .handlers.base import BaseResponse, BaseRequest
from .handlers.helpers import Version
class WaccaServlet:
class WaccaServlet(BaseServlet):
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
self.core_cfg = core_cfg
self.game_cfg = WaccaConfig()
@@ -62,15 +65,12 @@ class WaccaServlet:
coloredlogs.install(
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
)
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
return (
[],
[
("render_POST", "/WaccaServlet/api/{api}/{endpoint}", {}),
("render_POST", "/WaccaServlet/api/{api}/{branch}/{endpoint}", {})
]
)
def get_routes(self) -> List[Route]:
return [
Route("/WaccaServlet/api/{api:str}/{endpoint:str}", self.render_POST, methods=['POST']),
Route("/WaccaServlet/api/{api:str}/{branch:str}/{endpoint:str}", self.render_POST, methods=['POST']),
]
@classmethod
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
@@ -88,22 +88,22 @@ class WaccaServlet:
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
return (
f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/WaccaServlet",
self.core_cfg.title.hostname,
f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/WaccaServlet",
self.core_cfg.server.hostname,
)
return (f"http://{self.core_cfg.title.hostname}/WaccaServlet", self.core_cfg.title.hostname)
return (f"http://{self.core_cfg.server.hostname}/WaccaServlet", self.core_cfg.server.hostname)
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
async def render_POST(self, request: Request) -> bytes:
def end(resp: Dict) -> bytes:
hash = md5(json.dumps(resp, ensure_ascii=False).encode()).digest()
request.responseHeaders.addRawHeader(b"X-Wacca-Hash", hash.hex().encode())
return json.dumps(resp).encode()
return JSONResponse(resp, headers=["X-Wacca-Hash", hash.hex()])
api = matchers['api']
branch = matchers.get('branch', '')
endpoint = matchers['endpoint']
api = request.path_params.get('api', '')
branch = request.path_params.get('branch', '')
endpoint = request.path_params.get('endpoint', '')
client_ip = Utils.get_ip_addr(request)
bod = await request.body()
if branch:
url_path = f"{api}/{branch}/{endpoint}"
@@ -114,13 +114,13 @@ class WaccaServlet:
func_to_find = f"handle_{api}_{endpoint}_request"
try:
req_json = json.loads(request.content.getvalue())
req_json = json.loads(bod)
version_full = Version(req_json["appVersion"])
req = BaseRequest(req_json)
except KeyError as e:
self.logger.error(
f"Failed to parse request to {request.content.getvalue()} -> Missing required value {e}"
f"Failed to parse request to {bod} -> Missing required value {e}"
)
resp = BaseResponse()
resp.status = 1
@@ -129,7 +129,7 @@ class WaccaServlet:
except Exception as e:
self.logger.error(
f"Failed to parse request to {url_path} -> {request.content.getvalue()} -> {e}"
f"Failed to parse request to {url_path} -> {bod} -> {e}"
)
resp = BaseResponse()
resp.status = 1
@@ -176,7 +176,7 @@ class WaccaServlet:
try:
handler = getattr(self.versions[internal_ver], func_to_find)
resp = handler(req_json)
resp = await handler(req_json)
self.logger.debug(f"{req.appVersion} response {resp}")
return end(resp)

View File

@@ -37,13 +37,13 @@ class WaccaLily(WaccaS):
(210003, 0),
]
def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
async def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
resp = GetNewsResponseV3()
return resp.make()
def handle_user_status_create_request(self, data: Dict) -> Dict:
async def handle_user_status_create_request(self, data: Dict) -> Dict:
req = UserStatusCreateRequest(data)
ret = super().handle_user_status_create_request(data)
ret = await super().handle_user_status_create_request(data)
new_user = self.data.profile.get_profile(aime_id=req.aimeId)
@@ -64,7 +64,7 @@ class WaccaLily(WaccaS):
return ret
def handle_user_status_get_request(self, data: Dict) -> Dict:
async def handle_user_status_get_request(self, data: Dict) -> Dict:
req = UserStatusGetRequest(data)
resp = UserStatusGetV2Response()
@@ -145,7 +145,7 @@ class WaccaLily(WaccaS):
return resp.make()
def handle_user_status_login_request(self, data: Dict) -> Dict:
async def handle_user_status_login_request(self, data: Dict) -> Dict:
req = UserStatusLoginRequest(data)
resp = UserStatusLoginResponseV2()
is_consec_day = True
@@ -189,7 +189,7 @@ class WaccaLily(WaccaS):
return resp.make()
def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
async def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
req = UserStatusGetDetailRequest(data)
if req.appVersion.minor >= 53:
resp = UserStatusGetDetailResponseV3()
@@ -440,10 +440,10 @@ class WaccaLily(WaccaS):
return resp.make()
def handle_user_info_getMyroom_request(self, data: Dict) -> Dict:
async def handle_user_info_getMyroom_request(self, data: Dict) -> Dict:
return UserInfogetMyroomResponseV2().make()
def handle_user_status_update_request(self, data: Dict) -> Dict:
async def handle_user_status_update_request(self, data: Dict) -> Dict:
super().handle_user_status_update_request(data)
req = UserStatusUpdateRequestV2(data)
self.data.profile.update_profile_lastplayed(

View File

@@ -39,7 +39,7 @@ class WaccaLilyR(WaccaLily):
(210003, 0),
]
def handle_housing_start_request(self, data: Dict) -> Dict:
async def handle_housing_start_request(self, data: Dict) -> Dict:
req = HousingStartRequestV2(data)
allnet_region_id = None
@@ -71,9 +71,9 @@ class WaccaLilyR(WaccaLily):
resp = HousingStartResponseV1(region_id)
return resp.make()
def handle_user_status_create_request(self, data: Dict) -> Dict:
async def handle_user_status_create_request(self, data: Dict) -> Dict:
req = UserStatusCreateRequest(data)
resp = super().handle_user_status_create_request(data)
resp = await super().handle_user_status_create_request(data)
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210054
@@ -102,5 +102,5 @@ class WaccaLilyR(WaccaLily):
return resp
def handle_user_status_logout_request(self, data: Dict) -> Dict:
async def handle_user_status_logout_request(self, data: Dict) -> Dict:
return BaseResponse().make()

View File

@@ -47,12 +47,12 @@ class WaccaReverse(WaccaLilyR):
(310006, 0),
]
def handle_user_status_login_request(self, data: Dict) -> Dict:
resp = super().handle_user_status_login_request(data)
async def handle_user_status_login_request(self, data: Dict) -> Dict:
resp = await super().handle_user_status_login_request(data)
resp["params"].append([])
return resp
def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
async def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
req = UserStatusGetDetailRequest(data)
resp = UserStatusGetDetailResponseV4()
@@ -305,9 +305,9 @@ class WaccaReverse(WaccaLilyR):
return resp.make()
def handle_user_status_create_request(self, data: Dict) -> Dict:
async def handle_user_status_create_request(self, data: Dict) -> Dict:
req = UserStatusCreateRequest(data)
resp = super().handle_user_status_create_request(data)
resp = await super().handle_user_status_create_request(data)
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310001

View File

@@ -31,6 +31,6 @@ class WaccaS(WaccaBase):
super().__init__(cfg, game_cfg)
self.version = WaccaConstants.VER_WACCA_S
def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
async def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
resp = GetNewsResponseV2()
return resp.make()