Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
82004cb743 | |||
8f4c08f825 | |||
7ebd9bfb8a | |||
19c9740f4b | |||
1db020b5fc | |||
d1048694d4 | |||
944b80129b | |||
73dda06413 | |||
eacd4a2f43 | |||
dd33144040 |
@ -1,7 +1,5 @@
|
|||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from core.allnet import AllnetServlet, BillingServlet
|
|
||||||
from core.aimedb import AimedbServlette
|
from core.aimedb import AimedbServlette
|
||||||
from core.title import TitleServlet
|
from core.title import TitleServlet
|
||||||
from core.utils import Utils
|
from core.utils import Utils
|
||||||
from core.mucha import MuchaServlet
|
from core.mucha import MuchaServlet
|
||||||
from core.frontend import FrontendServlet
|
|
||||||
|
@ -120,7 +120,7 @@ class ADBHeader:
|
|||||||
if self.store_id == 0:
|
if self.store_id == 0:
|
||||||
raise ADBHeaderException(f"Store ID cannot be 0!")
|
raise ADBHeaderException(f"Store ID cannot be 0!")
|
||||||
|
|
||||||
if re.fullmatch(r"^A[0-9]{2}[E|X][0-9]{2}[A-HJ-NP-Z][0-9]{4}$", self.keychip_id) is None:
|
if re.fullmatch(r"^A[0-9]{2}[A-Z][0-9]{2}[A-HJ-NP-Z][0-9]{4}$", self.keychip_id) is None:
|
||||||
raise ADBHeaderException(f"Keychip ID {self.keychip_id} is invalid!")
|
raise ADBHeaderException(f"Keychip ID {self.keychip_id} is invalid!")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -9,7 +9,8 @@ from starlette.responses import PlainTextResponse
|
|||||||
from os import environ, path, mkdir, W_OK, access
|
from os import environ, path, mkdir, W_OK, access
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from core import CoreConfig, TitleServlet, MuchaServlet, AllnetServlet, BillingServlet, AimedbServlette
|
from core import CoreConfig, TitleServlet, MuchaServlet
|
||||||
|
from core.allnet import AllnetServlet, BillingServlet
|
||||||
from core.frontend import FrontendServlet
|
from core.frontend import FrontendServlet
|
||||||
|
|
||||||
async def dummy_rt(request: Request):
|
async def dummy_rt(request: Request):
|
||||||
|
@ -232,7 +232,7 @@ class ArcadeData(BaseData):
|
|||||||
return f"{platform_code}{'-' if dash else ''}{platform_rev:02d}{serial_letter}{serial_num:04d}{append:04d}"
|
return f"{platform_code}{'-' if dash else ''}{platform_rev:02d}{serial_letter}{serial_num:04d}{append:04d}"
|
||||||
|
|
||||||
def validate_keychip_format(self, serial: str) -> bool:
|
def validate_keychip_format(self, serial: str) -> bool:
|
||||||
# For the 2nd letter, E and X are the only "real" values that have been observed
|
# For the 2nd letter, E and X are the only "real" values that have been observed (A is used for generated keychips)
|
||||||
if re.fullmatch(r"^A[0-9]{2}[A-Z][-]?[0-9]{2}[A-HJ-NP-Z][0-9]{4}([0-9]{4})?$", serial) is None:
|
if re.fullmatch(r"^A[0-9]{2}[A-Z][-]?[0-9]{2}[A-HJ-NP-Z][0-9]{4}([0-9]{4})?$", serial) is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
from os import mkdir, path, access, W_OK
|
from os import mkdir, path, access, W_OK, environ
|
||||||
import yaml
|
import yaml
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
@ -25,10 +25,11 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument("action", type=str, help="create, upgrade, downgrade, create-owner, migrate, create-revision, create-autorevision")
|
parser.add_argument("action", type=str, help="create, upgrade, downgrade, create-owner, migrate, create-revision, create-autorevision")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
environ["ARTEMIS_CFG_DIR"] = args.config
|
||||||
|
|
||||||
cfg = CoreConfig()
|
cfg = CoreConfig()
|
||||||
if path.exists(f"{args.config}/core.yaml"):
|
if path.exists(f"{args.config}/core.yaml"):
|
||||||
cfg_dict = yaml.safe_load(open(f"{args.config}/core.yaml"))
|
cfg_dict = yaml.safe_load(open(f"{args.config}/core.yaml"))
|
||||||
cfg_dict.get("database", {})["loglevel"] = "info"
|
|
||||||
cfg.update(cfg_dict)
|
cfg.update(cfg_dict)
|
||||||
|
|
||||||
if not path.exists(cfg.server.log_dir):
|
if not path.exists(cfg.server.log_dir):
|
||||||
|
@ -80,12 +80,14 @@ The importer for Chunithm will import: Events, Music, Charge Items and Avatar Ac
|
|||||||
|
|
||||||
Config file is located in `config/chuni.yaml`.
|
Config file is located in `config/chuni.yaml`.
|
||||||
|
|
||||||
| Option | Info |
|
| Option | Info |
|
||||||
|------------------|----------------------------------------------------------------------------------------------------------------|
|
|------------------|---------------------------------------------------------------------------------------------------------------------|
|
||||||
| `news_msg` | If this is set, the news at the top of the main screen will be displayed (up to Chunithm Paradise Lost) |
|
| `news_msg` | If this is set, the news at the top of the main screen will be displayed (up to Chunithm Paradise Lost) |
|
||||||
| `name` | If this is set, all players that are not on a team will use this one by default. |
|
| `name` | If this is set, all players that are not on a team will use this one by default. |
|
||||||
| `use_login_bonus`| This is used to enable the login bonuses |
|
| `use_login_bonus`| This is used to enable the login bonuses |
|
||||||
| `crypto` | This option is used to enable the TLS Encryption |
|
| `stock_tickets` | If this is set, specifies tickets to auto-stock at login. Format is a comma-delimited list of IDs. Defaults to None |
|
||||||
|
| `stock_count` | Ignored if stock_tickets is not specified. Number to stock of each ticket. Defaults to 99 |
|
||||||
|
| `crypto` | This option is used to enable the TLS Encryption |
|
||||||
|
|
||||||
|
|
||||||
If you would like to use network encryption, add the keys to the `keys` section under `crypto`, where the key
|
If you would like to use network encryption, add the keys to the `keys` section under `crypto`, where the key
|
||||||
|
@ -8,7 +8,11 @@ team:
|
|||||||
|
|
||||||
mods:
|
mods:
|
||||||
use_login_bonus: True
|
use_login_bonus: True
|
||||||
|
# stock_tickets allows specified ticket IDs to be auto-stocked at login. Format is a comma-delimited string of ticket IDs
|
||||||
|
# note: quanity is not refreshed on "continue" after set - only on subsequent login
|
||||||
|
stock_tickets:
|
||||||
|
stock_count: 99
|
||||||
|
|
||||||
version:
|
version:
|
||||||
11:
|
11:
|
||||||
rom: 2.00.00
|
rom: 2.00.00
|
||||||
|
3
index.py
3
index.py
@ -6,7 +6,8 @@ import uvicorn
|
|||||||
import logging
|
import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from core import CoreConfig, AimedbServlette
|
from core.config import CoreConfig
|
||||||
|
from core.aimedb import AimedbServlette
|
||||||
|
|
||||||
async def launch_main(cfg: CoreConfig, ssl: bool) -> None:
|
async def launch_main(cfg: CoreConfig, ssl: bool) -> None:
|
||||||
if ssl:
|
if ssl:
|
||||||
|
@ -24,20 +24,35 @@ class ChuniBase:
|
|||||||
|
|
||||||
async 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
|
Handles the login bonus and ticket stock logic, required for the game
|
||||||
getUserLoginBonus gets called after getUserItem and therefore the
|
because getUserLoginBonus gets called after getUserItem; therefore the
|
||||||
items needs to be inserted in the database before they get requested.
|
items needs to be inserted in the database before they get requested.
|
||||||
|
|
||||||
Adds a bonusCount after a user logged in after 24 hours, makes sure
|
- Adds a stock for each specified ticket (itemKind 5)
|
||||||
loginBonus 30 gets looped, only show the login banner every 24 hours,
|
- Adds a bonusCount after a user logged in after 24 hours, makes sure
|
||||||
adds the bonus to items (itemKind 6)
|
loginBonus 30 gets looped, only show the login banner every 24 hours,
|
||||||
|
adds the bonus to items (itemKind 6)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
user_id = data["userId"]
|
||||||
|
|
||||||
|
# If we want to make certain tickets always available, stock them now
|
||||||
|
if self.game_cfg.mods.stock_tickets:
|
||||||
|
for ticket in self.game_cfg.mods.stock_tickets.split(","):
|
||||||
|
await self.data.item.put_item(
|
||||||
|
user_id,
|
||||||
|
{
|
||||||
|
"itemId": ticket.strip(),
|
||||||
|
"itemKind": 5,
|
||||||
|
"stock": self.game_cfg.mods.stock_count,
|
||||||
|
"isValid": True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
# ignore the login bonus if disabled in config
|
# ignore the login bonus if disabled in config
|
||||||
if not self.game_cfg.mods.use_login_bonus:
|
if not self.game_cfg.mods.use_login_bonus:
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
|
||||||
user_id = data["userId"]
|
|
||||||
login_bonus_presets = await self.data.static.get_login_bonus_presets(self.version)
|
login_bonus_presets = await self.data.static.get_login_bonus_presets(self.version)
|
||||||
|
|
||||||
for preset in login_bonus_presets:
|
for preset in login_bonus_presets:
|
||||||
|
@ -53,6 +53,18 @@ class ChuniModsConfig:
|
|||||||
self.__config, "chuni", "mods", "use_login_bonus", default=True
|
self.__config, "chuni", "mods", "use_login_bonus", default=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stock_tickets(self) -> str:
|
||||||
|
return CoreConfig.get_config_field(
|
||||||
|
self.__config, "chuni", "mods", "stock_tickets", default=None
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stock_count(self) -> int:
|
||||||
|
return CoreConfig.get_config_field(
|
||||||
|
self.__config, "chuni", "mods", "stock_count", default=99
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ChuniVersionConfig:
|
class ChuniVersionConfig:
|
||||||
def __init__(self, parent_config: "ChuniConfig") -> None:
|
def __init__(self, parent_config: "ChuniConfig") -> None:
|
||||||
|
@ -104,7 +104,8 @@ class ChuniNew(ChuniBase):
|
|||||||
return {"returnCode": "1"}
|
return {"returnCode": "1"}
|
||||||
|
|
||||||
async 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 = await self.data.item.get_map_areas(data["userId"])
|
map_area_ids = [int(area["mapAreaId"]) for area in data["mapAreaIdList"]]
|
||||||
|
user_map_areas = await self.data.item.get_map_areas(data["userId"], map_area_ids)
|
||||||
|
|
||||||
map_areas = []
|
map_areas = []
|
||||||
for map_area in user_map_areas:
|
for map_area in user_map_areas:
|
||||||
|
@ -35,11 +35,15 @@ class ChuniReader(BaseReader):
|
|||||||
|
|
||||||
if self.opt_dir is not None:
|
if self.opt_dir is not None:
|
||||||
data_dirs += self.get_data_directories(self.opt_dir)
|
data_dirs += self.get_data_directories(self.opt_dir)
|
||||||
|
|
||||||
|
we_diff = "4"
|
||||||
|
if self.version >= ChuniConstants.VER_CHUNITHM_NEW:
|
||||||
|
we_diff = "5"
|
||||||
|
|
||||||
for dir in data_dirs:
|
for dir in data_dirs:
|
||||||
self.logger.info(f"Read from {dir}")
|
self.logger.info(f"Read from {dir}")
|
||||||
await self.read_events(f"{dir}/event")
|
await self.read_events(f"{dir}/event")
|
||||||
await self.read_music(f"{dir}/music")
|
await self.read_music(f"{dir}/music", we_diff)
|
||||||
await self.read_charges(f"{dir}/chargeItem")
|
await self.read_charges(f"{dir}/chargeItem")
|
||||||
await self.read_avatar(f"{dir}/avatarAccessory")
|
await self.read_avatar(f"{dir}/avatarAccessory")
|
||||||
await self.read_login_bonus(f"{dir}/")
|
await self.read_login_bonus(f"{dir}/")
|
||||||
@ -138,7 +142,7 @@ class ChuniReader(BaseReader):
|
|||||||
else:
|
else:
|
||||||
self.logger.warning(f"Failed to insert event {id}")
|
self.logger.warning(f"Failed to insert event {id}")
|
||||||
|
|
||||||
async def read_music(self, music_dir: str) -> None:
|
async def read_music(self, music_dir: str, we_diff: str = "4") -> None:
|
||||||
for root, dirs, files in walk(music_dir):
|
for root, dirs, files in walk(music_dir):
|
||||||
for dir in dirs:
|
for dir in dirs:
|
||||||
if path.exists(f"{root}/{dir}/Music.xml"):
|
if path.exists(f"{root}/{dir}/Music.xml"):
|
||||||
@ -169,7 +173,7 @@ class ChuniReader(BaseReader):
|
|||||||
chart_type = MusicFumenData.find("type")
|
chart_type = MusicFumenData.find("type")
|
||||||
chart_id = chart_type.find("id").text
|
chart_id = chart_type.find("id").text
|
||||||
chart_diff = chart_type.find("str").text
|
chart_diff = chart_type.find("str").text
|
||||||
if chart_diff == "WorldsEnd" and (chart_id == "4" or chart_id == "5"): # 4 in SDBT, 5 in SDHD
|
if chart_diff == "WorldsEnd" and chart_id == we_diff: # 4 in SDBT, 5 in SDHD
|
||||||
level = float(xml_root.find("starDifType").text)
|
level = float(xml_root.find("starDifType").text)
|
||||||
we_chara = (
|
we_chara = (
|
||||||
xml_root.find("worldsEndTagName")
|
xml_root.find("worldsEndTagName")
|
||||||
|
@ -533,8 +533,8 @@ class ChuniItemData(BaseData):
|
|||||||
return None
|
return None
|
||||||
return result.lastrowid
|
return result.lastrowid
|
||||||
|
|
||||||
async def get_map_areas(self, user_id: int) -> Optional[List[Row]]:
|
async def get_map_areas(self, user_id: int, map_area_ids: List[int]) -> Optional[List[Row]]:
|
||||||
sql = select(map_area).where(map_area.c.user == user_id)
|
sql = select(map_area).where(map_area.c.user == user_id, map_area.c.mapAreaId.in_(map_area_ids))
|
||||||
|
|
||||||
result = await self.execute(sql)
|
result = await self.execute(sql)
|
||||||
if result is None:
|
if result is None:
|
||||||
|
@ -818,7 +818,8 @@ class Mai2Base:
|
|||||||
}
|
}
|
||||||
|
|
||||||
async 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)
|
self.logger.warning("Portrait uploading not supported at this time.")
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPortraitApi'}
|
||||||
|
|
||||||
async 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:
|
if not self.game_config.uploads.photos or not self.game_config.uploads.photos_dir:
|
||||||
|
Loading…
Reference in New Issue
Block a user