let black do it's magic

This commit is contained in:
Hay1tsme
2023-03-09 11:38:58 -05:00
parent fa7206848c
commit a76bb94eb1
150 changed files with 8474 additions and 4843 deletions

View File

@@ -9,4 +9,4 @@ database = WaccaData
reader = WaccaReader
frontend = WaccaFrontend
game_codes = [WaccaConstants.GAME_CODE]
current_schema_version = 3
current_schema_version = 3

File diff suppressed because it is too large Load Diff

View File

@@ -1,45 +1,65 @@
from typing import Dict, List
from core.config import CoreConfig
class WaccaServerConfig():
class WaccaServerConfig:
def __init__(self, parent_config: "WaccaConfig") -> None:
self.__config = parent_config
@property
def enable(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'wacca', 'server', 'enable', default=True)
return CoreConfig.get_config_field(
self.__config, "wacca", "server", "enable", default=True
)
@property
def loglevel(self) -> int:
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'wacca', 'server', 'loglevel', default="info"))
return CoreConfig.str_to_loglevel(
CoreConfig.get_config_field(
self.__config, "wacca", "server", "loglevel", default="info"
)
)
@property
def prefecture_name(self) -> str:
return CoreConfig.get_config_field(self.__config, 'wacca', 'server', 'prefecture_name', default="Hokkaido")
return CoreConfig.get_config_field(
self.__config, "wacca", "server", "prefecture_name", default="Hokkaido"
)
class WaccaModsConfig():
class WaccaModsConfig:
def __init__(self, parent_config: "WaccaConfig") -> None:
self.__config = parent_config
@property
def always_vip(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'wacca', 'mods', 'always_vip', default=True)
return CoreConfig.get_config_field(
self.__config, "wacca", "mods", "always_vip", default=True
)
@property
def infinite_tickets(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'wacca', 'mods', 'infinite_tickets', default=True)
return CoreConfig.get_config_field(
self.__config, "wacca", "mods", "infinite_tickets", default=True
)
@property
def infinite_wp(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'wacca', 'mods', 'infinite_wp', default=True)
return CoreConfig.get_config_field(
self.__config, "wacca", "mods", "infinite_wp", default=True
)
class WaccaGateConfig():
class WaccaGateConfig:
def __init__(self, parent_config: "WaccaConfig") -> None:
self.__config = parent_config
@property
def enabled_gates(self) -> List[int]:
return CoreConfig.get_config_field(self.__config, 'wacca', 'gates', 'enabled_gates', default=[])
return CoreConfig.get_config_field(
self.__config, "wacca", "gates", "enabled_gates", default=[]
)
class WaccaConfig(dict):
def __init__(self) -> None:

View File

@@ -3,7 +3,8 @@ from typing import Optional
from core.const import AllnetJapanRegionId
class WaccaConstants():
class WaccaConstants:
CONFIG_NAME = "wacca.yaml"
GAME_CODE = "SDFE"
@@ -51,51 +52,48 @@ class WaccaConstants():
}
OPTIONS = {
"note_speed": 1, # 1.0 - 6.0
"field_mask": 2, # 0-4
"note_sound": 3, # ID
"note_color": 4, # ID
"bgm_volume": 5, # 0-100 incremements of 10
"bg_video": 7, # ask, on, or off
"mirror": 101, # none or left+right swap
"judge_display_pos": 102, # center, under, over, top or off
"judge_detail_display": 103, # on or off
"measure_guidelines": 105, # on or off
"guideline_mask": 106, # 0 - 5
"judge_line_timing_adjust": 108, # -10 - 10
"note_design": 110, # 1 - 5
"bonus_effect": 114, # on or off
"chara_voice": 115, # "usually" or none
"score_display_method": 116, # add or subtract
"give_up": 117, # off, no touch, can't achieve s, ss, sss, pb
"guideline_spacing": 118, # none, or a-g type
"center_display": 119, # none, combo, score add, score sub, s ss sss pb boarder
"ranking_display": 120, # on or off
"stage_up_icon_display": 121, # on or off
"rating_display": 122, # on or off
"player_level_display": 123, # on or off
"touch_effect": 124, # on or off
"guide_sound_vol": 125, # 0-100 incremements of 10
"touch_note_vol": 126, # 0-100 incremements of 10
"hold_note_vol": 127, # 0-100 incremements of 10
"slide_note_vol": 128, # 0-100 incremements of 10
"snap_note_vol": 129, # 0-100 incremements of 10
"chain_note_vol": 130, # 0-100 incremements of 10
"bonus_note_vol": 131, # 0-100 incremements of 10
"gate_skip": 132, # on or off
"key_beam_display": 133, # on or off
"left_slide_note_color": 201, # red blue green or orange
"right_slide_note_color": 202, # red blue green or orange
"forward_slide_note_color": 203, # red blue green or orange
"back_slide_note_color": 204, # red blue green or orange
"master_vol": 1001, # 0-100 incremements of 10
"set_title_id": 1002, # ID
"set_icon_id": 1003, # ID
"set_nav_id": 1004, # ID
"set_plate_id": 1005, # ID
"note_speed": 1, # 1.0 - 6.0
"field_mask": 2, # 0-4
"note_sound": 3, # ID
"note_color": 4, # ID
"bgm_volume": 5, # 0-100 incremements of 10
"bg_video": 7, # ask, on, or off
"mirror": 101, # none or left+right swap
"judge_display_pos": 102, # center, under, over, top or off
"judge_detail_display": 103, # on or off
"measure_guidelines": 105, # on or off
"guideline_mask": 106, # 0 - 5
"judge_line_timing_adjust": 108, # -10 - 10
"note_design": 110, # 1 - 5
"bonus_effect": 114, # on or off
"chara_voice": 115, # "usually" or none
"score_display_method": 116, # add or subtract
"give_up": 117, # off, no touch, can't achieve s, ss, sss, pb
"guideline_spacing": 118, # none, or a-g type
"center_display": 119, # none, combo, score add, score sub, s ss sss pb boarder
"ranking_display": 120, # on or off
"stage_up_icon_display": 121, # on or off
"rating_display": 122, # on or off
"player_level_display": 123, # on or off
"touch_effect": 124, # on or off
"guide_sound_vol": 125, # 0-100 incremements of 10
"touch_note_vol": 126, # 0-100 incremements of 10
"hold_note_vol": 127, # 0-100 incremements of 10
"slide_note_vol": 128, # 0-100 incremements of 10
"snap_note_vol": 129, # 0-100 incremements of 10
"chain_note_vol": 130, # 0-100 incremements of 10
"bonus_note_vol": 131, # 0-100 incremements of 10
"gate_skip": 132, # on or off
"key_beam_display": 133, # on or off
"left_slide_note_color": 201, # red blue green or orange
"right_slide_note_color": 202, # red blue green or orange
"forward_slide_note_color": 203, # red blue green or orange
"back_slide_note_color": 204, # red blue green or orange
"master_vol": 1001, # 0-100 incremements of 10
"set_title_id": 1002, # ID
"set_icon_id": 1003, # ID
"set_nav_id": 1004, # ID
"set_plate_id": 1005, # ID
}
class Difficulty(Enum):
@@ -103,7 +101,7 @@ class WaccaConstants():
HARD = 2
EXPERT = 3
INFERNO = 4
class Region(Enum):
NONE = 0
HOKKAIDO = 1
@@ -163,7 +161,7 @@ class WaccaConstants():
SGP = 51
KOREA = 52
KOR = 52
VALID_COUNTRIES = set(["JPN", "USA", "KOR", "HKG", "SGP"])
@classmethod
@@ -174,16 +172,54 @@ class WaccaConstants():
def allnet_region_id_to_wacca_region(cls, region: int) -> Optional[Region]:
try:
return [
cls.Region.NONE, cls.Region.AICHI, cls.Region.AOMORI, cls.Region.AKITA, cls.Region.ISHIKAWA,
cls.Region.IBARAKI, cls.Region.IWATE, cls.Region.EHIME, cls.Region.OITA, cls.Region.OSAKA,
cls.Region.OKAYAMA, cls.Region.OKINAWA, cls.Region.KAGAWA, cls.Region.KAGOSHIMA, cls.Region.KANAGAWA,
cls.Region.GIFU, cls.Region.KYOTO, cls.Region.KUMAMOTO, cls.Region.GUNMA, cls.Region.KOCHI,
cls.Region.SAITAMA, cls.Region.SAGA, cls.Region.SHIGA, cls.Region.SHIZUOKA, cls.Region.SHIMANE,
cls.Region.CHIBA, cls.Region.TOKYO, cls.Region.TOKUSHIMA, cls.Region.TOCHIGI, cls.Region.TOTTORI,
cls.Region.TOYAMA, cls.Region.NAGASAKI, cls.Region.NAGANO, cls.Region.NARA, cls.Region.NIIGATA,
cls.Region.HYOGO, cls.Region.HIROSHIMA, cls.Region.FUKUI, cls.Region.FUKUOKA, cls.Region.FUKUSHIMA,
cls.Region.HOKKAIDO, cls.Region.MIE, cls.Region.MIYAGI, cls.Region.MIYAZAKI, cls.Region.YAMAGATA,
cls.Region.YAMAGUCHI, cls.Region.YAMANASHI, cls.Region.WAKAYAMA,
cls.Region.NONE,
cls.Region.AICHI,
cls.Region.AOMORI,
cls.Region.AKITA,
cls.Region.ISHIKAWA,
cls.Region.IBARAKI,
cls.Region.IWATE,
cls.Region.EHIME,
cls.Region.OITA,
cls.Region.OSAKA,
cls.Region.OKAYAMA,
cls.Region.OKINAWA,
cls.Region.KAGAWA,
cls.Region.KAGOSHIMA,
cls.Region.KANAGAWA,
cls.Region.GIFU,
cls.Region.KYOTO,
cls.Region.KUMAMOTO,
cls.Region.GUNMA,
cls.Region.KOCHI,
cls.Region.SAITAMA,
cls.Region.SAGA,
cls.Region.SHIGA,
cls.Region.SHIZUOKA,
cls.Region.SHIMANE,
cls.Region.CHIBA,
cls.Region.TOKYO,
cls.Region.TOKUSHIMA,
cls.Region.TOCHIGI,
cls.Region.TOTTORI,
cls.Region.TOYAMA,
cls.Region.NAGASAKI,
cls.Region.NAGANO,
cls.Region.NARA,
cls.Region.NIIGATA,
cls.Region.HYOGO,
cls.Region.HIROSHIMA,
cls.Region.FUKUI,
cls.Region.FUKUOKA,
cls.Region.FUKUSHIMA,
cls.Region.HOKKAIDO,
cls.Region.MIE,
cls.Region.MIYAGI,
cls.Region.MIYAZAKI,
cls.Region.YAMAGATA,
cls.Region.YAMAGUCHI,
cls.Region.YAMANASHI,
cls.Region.WAKAYAMA,
][region]
except: return None
except:
return None

View File

@@ -2,6 +2,7 @@ from core.data import Data
from core.config import CoreConfig
from titles.wacca.schema import *
class WaccaData(Data):
def __init__(self, cfg: CoreConfig) -> None:
super().__init__(cfg)
@@ -9,4 +10,4 @@ class WaccaData(Data):
self.profile = WaccaProfileData(self.config, self.session)
self.score = WaccaScoreData(self.config, self.session)
self.item = WaccaItemData(self.config, self.session)
self.static = WaccaStaticData(self.config, self.session)
self.static = WaccaStaticData(self.config, self.session)

View File

@@ -8,17 +8,22 @@ from titles.wacca.database import WaccaData
from titles.wacca.config import WaccaConfig
from titles.wacca.const import WaccaConstants
class WaccaFrontend(FE_Base):
def __init__(self, cfg: CoreConfig, environment: jinja2.Environment, cfg_dir: str) -> None:
def __init__(
self, cfg: CoreConfig, environment: jinja2.Environment, cfg_dir: str
) -> None:
super().__init__(cfg, environment)
self.data = WaccaData(cfg)
self.game_cfg = WaccaConfig()
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/wacca.yaml")))
self.nav_name = "Wacca"
def render_GET(self, request: Request) -> bytes:
template = self.environment.get_template("titles/wacca/frontend/wacca_index.jinja")
template = self.environment.get_template(
"titles/wacca/frontend/wacca_index.jinja"
)
return template.render(
title=f"{self.core_config.server.name} | {self.nav_name}",
game_list=self.environment.globals["game_list"]
game_list=self.environment.globals["game_list"],
).encode("utf-16")

View File

@@ -6,4 +6,4 @@ from titles.wacca.handlers.user_misc import *
from titles.wacca.handlers.user_music import *
from titles.wacca.handlers.user_status import *
from titles.wacca.handlers.user_trial import *
from titles.wacca.handlers.user_vip import *
from titles.wacca.handlers.user_vip import *

View File

@@ -3,6 +3,7 @@ from typing import List, Dict
from titles.wacca.handlers.base import BaseResponse, BaseRequest
from titles.wacca.handlers.helpers import Notice
# ---advertise/GetNews---
class GetNewsResponseV1(BaseResponse):
def __init__(self) -> None:
@@ -19,27 +20,29 @@ class GetNewsResponseV1(BaseResponse):
for notice in self.notices:
note.append(notice.make())
self.params = [
note,
self.copywrightListings,
self.stoppedSongs,
self.stoppedJackets,
self.stoppedMovies,
self.stoppedIcons
self.params = [
note,
self.copywrightListings,
self.stoppedSongs,
self.stoppedJackets,
self.stoppedMovies,
self.stoppedIcons,
]
return super().make()
class GetNewsResponseV2(GetNewsResponseV1):
class GetNewsResponseV2(GetNewsResponseV1):
stoppedProducts: list[int] = []
def make(self) -> Dict:
super().make()
self.params.append(self.stoppedProducts)
return super(GetNewsResponseV1, self).make()
class GetNewsResponseV3(GetNewsResponseV2):
stoppedNavs: list[int] = []
stoppedNavVoices: list[int] = []
@@ -48,18 +51,20 @@ class GetNewsResponseV3(GetNewsResponseV2):
super().make()
self.params.append(self.stoppedNavs)
self.params.append(self.stoppedNavVoices)
return super(GetNewsResponseV1, self).make()
# ---advertise/GetRanking---
class AdvertiseGetRankingRequest(BaseRequest):
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.resourceVer: int = self.params[0]
class AdvertiseGetRankingResponse(BaseResponse):
def __init__(self) -> None:
super().__init__()
def make(self) -> Dict:
return super().make()
return super().make()

View File

@@ -2,7 +2,8 @@ from typing import Dict, List
from titles.wacca.handlers.helpers import Version
from datetime import datetime
class BaseRequest():
class BaseRequest:
def __init__(self, data: Dict) -> None:
self.requestNo: int = data["requestNo"]
self.appVersion: Version = Version(data["appVersion"])
@@ -10,7 +11,8 @@ class BaseRequest():
self.chipId: str = data["chipId"]
self.params: List = data["params"]
class BaseResponse():
class BaseResponse:
def __init__(self) -> None:
self.status: int = 0
self.message: str = ""
@@ -28,5 +30,5 @@ class BaseResponse():
"maintNoticeTime": self.maintNoticeTime,
"maintNotPlayableTime": self.maintNotPlayableTime,
"maintStartTime": self.maintStartTime,
"params": self.params
"params": self.params,
}

View File

@@ -3,28 +3,33 @@ from enum import Enum
from titles.wacca.const import WaccaConstants
class ShortVersion:
def __init__(self, version: str = "", major = 1, minor = 0, patch = 0) -> None:
def __init__(self, version: str = "", major=1, minor=0, patch=0) -> None:
split = version.split(".")
if len(split) >= 3:
self.major = int(split[0])
self.minor = int(split[1])
self.patch = int(split[2])
else:
else:
self.major = major
self.minor = minor
self.patch = patch
def __str__(self) -> str:
return f"{self.major}.{self.minor}.{self.patch}"
def __int__(self) -> int:
return (self.major * 10000) + (self.minor * 100) + self.patch
def __eq__(self, other: "ShortVersion"):
return self.major == other.major and self.minor == other.minor and self.patch == other.patch
return (
self.major == other.major
and self.minor == other.minor
and self.patch == other.patch
)
def __gt__(self, other: "ShortVersion"):
if self.major > other.major:
return True
@@ -34,9 +39,9 @@ class ShortVersion:
elif self.minor == other.minor:
if self.patch > other.patch:
return True
return False
def __ge__(self, other: "ShortVersion"):
if self.major > other.major:
return True
@@ -46,9 +51,9 @@ class ShortVersion:
elif self.minor == other.minor:
if self.patch > other.patch or self.patch == other.patch:
return True
return False
def __lt__(self, other: "ShortVersion"):
if self.major < other.major:
return True
@@ -58,9 +63,9 @@ class ShortVersion:
elif self.minor == other.minor:
if self.patch < other.patch:
return True
return False
def __le__(self, other: "ShortVersion"):
if self.major < other.major:
return True
@@ -70,39 +75,45 @@ class ShortVersion:
elif self.minor == other.minor:
if self.patch < other.patch or self.patch == other.patch:
return True
return False
class Version(ShortVersion):
def __init__(self, version = "", major = 1, minor = 0, patch = 0, country = "JPN", build = 0, role = "C") -> None:
def __init__(
self, version="", major=1, minor=0, patch=0, country="JPN", build=0, role="C"
) -> None:
super().__init__(version, major, minor, patch)
split = version.split(".")
if len(split) >= 6:
self.country: str = split[3]
self.country: str = split[3]
self.build = int(split[4])
self.role: str = split[5]
else:
self.country = country
self.build = build
self.role = role
def __str__(self) -> str:
return f"{self.major}.{self.minor}.{self.patch}.{self.country}.{self.role}.{self.build}"
class HousingInfo:
"""
1 is lan install role, 2 is country
"""
id: int = 0
val: str = ""
def __init__(self, id: int = 0, val: str = "") -> None:
self.id = id
self.val = val
def make(self) -> List:
return [ self.id, self.val ]
return [self.id, self.val]
class Notice:
name: str = ""
@@ -116,25 +127,44 @@ class Notice:
endTime: int = 0
voiceline: int = 0
def __init__(self, name: str = "", title: str = "", message: str = "", start: int = 0, end: int = 0) -> None:
def __init__(
self,
name: str = "",
title: str = "",
message: str = "",
start: int = 0,
end: int = 0,
) -> None:
self.name = name
self.title = title
self.message = message
self.startTime = start
self.endTime = end
def make(self) -> List:
return [ self.name, self.title, self.message, self.unknown3, self.unknown4, int(self.showTitleScreen),
int(self.showWelcomeScreen), self.startTime, self.endTime, self.voiceline]
return [
self.name,
self.title,
self.message,
self.unknown3,
self.unknown4,
int(self.showTitleScreen),
int(self.showWelcomeScreen),
self.startTime,
self.endTime,
self.voiceline,
]
class UserOption:
def __init__(self, opt_id: int = 0, opt_val: Any = 0) -> None:
self.opt_id = opt_id
self.opt_val = opt_val
def make(self) -> List:
return [self.opt_id, self.opt_val]
class UserStatusV1:
def __init__(self) -> None:
self.userId: int = 0
@@ -160,19 +190,20 @@ class UserStatusV1:
self.useCount,
]
class UserStatusV2(UserStatusV1):
def __init__(self) -> None:
super().__init__()
super().__init__()
self.loginDays: int = 0
self.loginConsecutive: int = 0
self.loginConsecutiveDays: int = 0
self.loginsToday: int = 0
self.rating: int = 0
self.rating: int = 0
self.vipExpireTime: int = 0
def make(self) -> List:
ret = super().make()
ret.append(self.loginDays)
ret.append(self.loginConsecutive)
ret.append(self.loginConsecutiveDays)
@@ -182,17 +213,20 @@ class UserStatusV2(UserStatusV1):
return ret
class ProfileStatus(Enum):
ProfileGood = 0
ProfileRegister = 1
ProfileInUse = 2
ProfileWrongRegion = 3
class PlayVersionStatus(Enum):
VersionGood = 0
VersionTooNew = 1
VersionUpgrade = 2
class PlayModeCounts:
seasonId: int = 0
modeId: int = 0
@@ -202,13 +236,10 @@ class PlayModeCounts:
self.seasonId = seasonId
self.modeId = modeId
self.playNum = playNum
def make(self) -> List:
return [
self.seasonId,
self.modeId,
self.playNum
]
return [self.seasonId, self.modeId, self.playNum]
class SongUnlock:
songId: int = 0
@@ -216,76 +247,72 @@ class SongUnlock:
whenAppeared: int = 0
whenUnlocked: int = 0
def __init__(self, song_id: int = 0, difficulty: int = 1, whenAppered: int = 0, whenUnlocked: int = 0) -> None:
def __init__(
self,
song_id: int = 0,
difficulty: int = 1,
whenAppered: int = 0,
whenUnlocked: int = 0,
) -> None:
self.songId = song_id
self.difficulty = difficulty
self.whenAppeared = whenAppered
self.whenUnlocked = whenUnlocked
def make(self) -> List:
return [
self.songId,
self.difficulty,
self.whenAppeared,
self.whenUnlocked
]
return [self.songId, self.difficulty, self.whenAppeared, self.whenUnlocked]
class GenericItemRecv:
def __init__(self, item_type: int = 1, item_id: int = 1, quantity: int = 1) -> None:
self.itemId = item_id
self.itemType = item_type
self.quantity = quantity
def make(self) -> List:
return [ self.itemType, self.itemId, self.quantity ]
return [self.itemType, self.itemId, self.quantity]
class GenericItemSend:
def __init__(self, itemId: int, itemType: int, whenAcquired: int) -> None:
self.itemId = itemId
self.itemType = itemType
self.whenAcquired = whenAcquired
def make(self) -> List:
return [
self.itemId,
self.itemType,
self.whenAcquired
]
return [self.itemId, self.itemType, self.whenAcquired]
class IconItem(GenericItemSend):
uses: int = 0
def __init__(self, itemId: int, itemType: int, uses: int, whenAcquired: int) -> None:
def __init__(
self, itemId: int, itemType: int, uses: int, whenAcquired: int
) -> None:
super().__init__(itemId, itemType, whenAcquired)
self.uses = uses
def make(self) -> List:
return [
self.itemId,
self.itemType,
self.uses,
self.whenAcquired
]
return [self.itemId, self.itemType, self.uses, self.whenAcquired]
class TrophyItem:
trophyId: int = 0
trophyId: int = 0
season: int = 1
progress: int = 0
badgeType: int = 0
def __init__(self, trophyId: int, season: int, progress: int, badgeType: int) -> None:
def __init__(
self, trophyId: int, season: int, progress: int, badgeType: int
) -> None:
self.trophyId = trophyId
self.season = season
self.season = season
self.progress = progress
self.badgeType = badgeType
def make(self) -> List:
return [
self.trophyId,
self.season,
self.progress,
self.badgeType
]
return [self.trophyId, self.season, self.progress, self.badgeType]
class TicketItem:
userTicketId: int = 0
@@ -296,18 +323,17 @@ class TicketItem:
self.userTicketId = userTicketId
self.ticketId = ticketId
self.whenExpires = whenExpires
def make(self) -> List:
return [
self.userTicketId,
self.ticketId,
self.whenExpires
]
return [self.userTicketId, self.ticketId, self.whenExpires]
class NavigatorItem(IconItem):
usesToday: int = 0
def __init__(self, itemId: int, itemType: int, whenAcquired: int, uses: int, usesToday: int) -> None:
def __init__(
self, itemId: int, itemType: int, whenAcquired: int, uses: int, usesToday: int
) -> None:
super().__init__(itemId, itemType, uses, whenAcquired)
self.usesToday = usesToday
@@ -317,9 +343,10 @@ class NavigatorItem(IconItem):
self.itemType,
self.whenAcquired,
self.uses,
self.usesToday
self.usesToday,
]
class SkillItem:
skill_type: int
level: int
@@ -327,12 +354,8 @@ class SkillItem:
badge: int
def make(self) -> List:
return [
self.skill_type,
self.level,
self.flag,
self.badge
]
return [self.skill_type, self.level, self.flag, self.badge]
class UserItemInfoV1:
def __init__(self) -> None:
@@ -383,6 +406,7 @@ class UserItemInfoV1:
sounds,
]
class UserItemInfoV2(UserItemInfoV1):
def __init__(self) -> None:
super().__init__()
@@ -391,18 +415,19 @@ class UserItemInfoV2(UserItemInfoV1):
def make(self) -> List:
ret = super().make()
plates = []
plates = []
navs = []
for x in self.navigators:
navs.append(x.make())
for x in self.plates:
plates.append(x.make())
ret.append(navs)
ret.append(plates)
return ret
class UserItemInfoV3(UserItemInfoV2):
def __init__(self) -> None:
super().__init__()
@@ -414,29 +439,44 @@ class UserItemInfoV3(UserItemInfoV2):
for x in self.touchEffect:
effect.append(x.make())
ret.append(effect)
return ret
class SongDetailClearCounts:
def __init__(self, play_ct: int = 0, clear_ct: int = 0, ml_ct: int = 0, fc_ct: int = 0,
am_ct: int = 0, counts: Optional[List[int]] = None) -> None:
class SongDetailClearCounts:
def __init__(
self,
play_ct: int = 0,
clear_ct: int = 0,
ml_ct: int = 0,
fc_ct: int = 0,
am_ct: int = 0,
counts: Optional[List[int]] = None,
) -> None:
if counts is None:
self.playCt = play_ct
self.clearCt = clear_ct
self.misslessCt = ml_ct
self.fullComboCt = fc_ct
self.allMarvelousCt = am_ct
else:
self.playCt = counts[0]
self.clearCt = counts[1]
self.misslessCt = counts[2]
self.fullComboCt = counts[3]
self.allMarvelousCt = counts[4]
def make(self) -> List:
return [self.playCt, self.clearCt, self.misslessCt, self.fullComboCt, self.allMarvelousCt]
return [
self.playCt,
self.clearCt,
self.misslessCt,
self.fullComboCt,
self.allMarvelousCt,
]
class SongDetailGradeCountsV1:
dCt: int
@@ -450,8 +490,20 @@ class SongDetailGradeCountsV1:
sssCt: int
masterCt: int
def __init__(self, d: int = 0, c: int = 0, b: int = 0, a: int = 0, aa: int = 0, aaa: int = 0, s: int = 0,
ss: int = 0, sss: int = 0, master: int = 0, counts: Optional[List[int]] = None) -> None:
def __init__(
self,
d: int = 0,
c: int = 0,
b: int = 0,
a: int = 0,
aa: int = 0,
aaa: int = 0,
s: int = 0,
ss: int = 0,
sss: int = 0,
master: int = 0,
counts: Optional[List[int]] = None,
) -> None:
if counts is None:
self.dCt = d
self.cCt = c
@@ -463,7 +515,7 @@ class SongDetailGradeCountsV1:
self.ssCt = ss
self.sssCt = sss
self.masterCt = master
else:
self.dCt = counts[0]
self.cCt = counts[1]
@@ -474,24 +526,51 @@ class SongDetailGradeCountsV1:
self.sCt = counts[6]
self.ssCt = counts[7]
self.sssCt = counts[8]
self.masterCt =counts[9]
self.masterCt = counts[9]
def make(self) -> List:
return [self.dCt, self.cCt, self.bCt, self.aCt, self.aaCt, self.aaaCt, self.sCt, self.ssCt, self.sssCt, self.masterCt]
return [
self.dCt,
self.cCt,
self.bCt,
self.aCt,
self.aaCt,
self.aaaCt,
self.sCt,
self.ssCt,
self.sssCt,
self.masterCt,
]
class SongDetailGradeCountsV2(SongDetailGradeCountsV1):
spCt: int
sspCt: int
ssspCt: int
def __init__(self, d: int = 0, c: int = 0, b: int = 0, a: int = 0, aa: int = 0, aaa: int = 0, s: int = 0,
ss: int = 0, sss: int = 0, master: int = 0, sp: int = 0, ssp: int = 0, sssp: int = 0, counts: Optional[List[int]] = None) -> None:
def __init__(
self,
d: int = 0,
c: int = 0,
b: int = 0,
a: int = 0,
aa: int = 0,
aaa: int = 0,
s: int = 0,
ss: int = 0,
sss: int = 0,
master: int = 0,
sp: int = 0,
ssp: int = 0,
sssp: int = 0,
counts: Optional[List[int]] = None,
) -> None:
super().__init__(d, c, b, a, aa, aaa, s, ss, sss, master, counts)
if counts is None:
self.spCt = sp
self.sspCt = ssp
self.ssspCt = sssp
else:
self.spCt = counts[10]
self.sspCt = counts[11]
@@ -500,6 +579,7 @@ class SongDetailGradeCountsV2(SongDetailGradeCountsV1):
def make(self) -> List:
return super().make() + [self.spCt, self.sspCt, self.ssspCt]
class BestScoreDetailV1:
songId: int = 0
difficulty: int = 1
@@ -527,49 +607,59 @@ class BestScoreDetailV1:
self.bestCombo,
self.lowestMissCtMaybe,
self.isUnlock,
self.rating
self.rating,
]
class BestScoreDetailV2(BestScoreDetailV1):
gradeCounts: SongDetailGradeCountsV2 = SongDetailGradeCountsV2()
class SongUpdateJudgementCounts:
marvCt: int
greatCt: int
goodCt: int
missCt: int
def __init__(self, marvs: int = 0, greats: int = 0, goods: int = 0, misses: int = 0) -> None:
def __init__(
self, marvs: int = 0, greats: int = 0, goods: int = 0, misses: int = 0
) -> None:
self.marvCt = marvs
self.greatCt = greats
self.goodCt = goods
self.missCt = misses
def make(self) -> List:
return [self.marvCt, self.greatCt, self.goodCt, self.missCt]
class SongUpdateDetailV1:
def __init__(self, data: List) -> None:
def __init__(self, data: List) -> None:
if data is not None:
self.songId = data[0]
self.difficulty = data[1]
self.level = data[2]
self.score = data[3]
self.judgements = SongUpdateJudgementCounts(data[4][0], data[4][1], data[4][2], data[4][3])
self.judgements = SongUpdateJudgementCounts(
data[4][0], data[4][1], data[4][2], data[4][3]
)
self.maxCombo = data[5]
self.grade = WaccaConstants.GRADES(data[6]) # .value to get number, .name to get letter
self.grade = WaccaConstants.GRADES(
data[6]
) # .value to get number, .name to get letter
self.flagCleared = False if data[7] == 0 else True
self.flagMissless = False if data[8] == 0 else True
self.flagFullcombo = False if data[9] == 0 else True
self.flagAllMarvelous = False if data[10] == 0 else True
self.flagGiveUp = False if data[11] == 0 else True
self.skillPt = data[12]
self.skillPt = data[12]
self.fastCt = 0
self.slowCt = 0
self.flagNewRecord = False
class SongUpdateDetailV2(SongUpdateDetailV1):
def __init__(self, data: List) -> None:
super().__init__(data)
@@ -578,6 +668,7 @@ class SongUpdateDetailV2(SongUpdateDetailV1):
self.slowCt = data[14]
self.flagNewRecord = False if data[15] == 0 else True
class SeasonalInfoV1:
def __init__(self) -> None:
self.level: int = 0
@@ -586,7 +677,7 @@ class SeasonalInfoV1:
self.cumulativeScore: int = 0
self.titlesObtained: int = 0
self.iconsObtained: int = 0
self.skillPts: int = 0
self.skillPts: int = 0
self.noteColorsObtained: int = 0
self.noteSoundsObtained: int = 0
@@ -600,9 +691,10 @@ class SeasonalInfoV1:
self.iconsObtained,
self.skillPts,
self.noteColorsObtained,
self.noteSoundsObtained
self.noteSoundsObtained,
]
class SeasonalInfoV2(SeasonalInfoV1):
def __init__(self) -> None:
super().__init__()
@@ -612,6 +704,7 @@ class SeasonalInfoV2(SeasonalInfoV1):
def make(self) -> List:
return super().make() + [self.platesObtained, self.cumulativeGatePts]
class BingoPageStatus:
id = 0
location = 1
@@ -625,23 +718,30 @@ class BingoPageStatus:
def make(self) -> List:
return [self.id, self.location, self.progress]
class BingoDetail:
def __init__(self, pageNumber: int) -> None:
self.pageNumber = pageNumber
self.pageStatus: List[BingoPageStatus] = []
def make(self) -> List:
status = []
for x in self.pageStatus:
status.append(x.make())
return [
self.pageNumber,
status
]
return [self.pageNumber, status]
class GateDetailV1:
def __init__(self, gate_id: int = 1, page: int = 1, progress: int = 0, loops: int = 0, last_used: int = 0, mission_flg = 0) -> None:
def __init__(
self,
gate_id: int = 1,
page: int = 1,
progress: int = 0,
loops: int = 0,
last_used: int = 0,
mission_flg=0,
) -> None:
self.id = gate_id
self.page = page
self.progress = progress
@@ -652,14 +752,17 @@ class GateDetailV1:
def make(self) -> List:
return [self.id, 1, self.page, self.progress, self.loops, self.lastUsed]
class GateDetailV2(GateDetailV1):
def make(self) -> List:
return super().make() + [self.missionFlg]
class GachaInfo:
def make(self) -> List:
return []
class LastSongDetail:
lastSongId = 90
lastSongDiff = 1
@@ -667,8 +770,14 @@ class LastSongDetail:
lastFolderId = 1
lastSongOrd = 1
def __init__(self, last_song: int = 90, last_diff: int = 1, last_folder_ord: int = 1,
last_folder_id: int = 1, last_song_ord: int = 1) -> None:
def __init__(
self,
last_song: int = 90,
last_diff: int = 1,
last_folder_ord: int = 1,
last_folder_id: int = 1,
last_song_ord: int = 1,
) -> None:
self.lastSongId = last_song
self.lastSongDiff = last_diff
self.lastFolderOrd = last_folder_ord
@@ -676,13 +785,20 @@ class LastSongDetail:
self.lastSongOrd = last_song_ord
def make(self) -> List:
return [self.lastSongId, self.lastSongDiff, self.lastFolderOrd, self.lastFolderId,
self.lastSongOrd]
return [
self.lastSongId,
self.lastSongDiff,
self.lastFolderOrd,
self.lastFolderId,
self.lastSongOrd,
]
class FriendDetail:
def make(self) -> List:
return []
class LoginBonusInfo:
def __init__(self) -> None:
self.tickets: List[TicketItem] = []
@@ -695,27 +811,38 @@ class LoginBonusInfo:
for ticket in self.tickets:
tks.append(ticket.make())
for item in self.items:
itms.append(item.make())
return [ tks, itms, self.message ]
return [tks, itms, self.message]
class VipLoginBonus:
id = 1
unknown = 0
item: GenericItemRecv
def __init__(self, id: int = 1, unk: int = 0, item_type: int = 1, item_id: int = 1, item_qt: int = 1) -> None:
def __init__(
self,
id: int = 1,
unk: int = 0,
item_type: int = 1,
item_id: int = 1,
item_qt: int = 1,
) -> None:
self.id = id
self.unknown = unk
self.item = GenericItemRecv(item_type, item_id, item_qt)
def make(self) -> List:
return [ self.id, self.unknown, self.item.make() ]
return [self.id, self.unknown, self.item.make()]
class VipInfo:
def __init__(self, year: int = 2019, month: int = 1, day: int = 1, num_item: int = 1) -> None:
def __init__(
self, year: int = 2019, month: int = 1, day: int = 1, num_item: int = 1
) -> None:
self.pageYear = year
self.pageMonth = month
self.pageDay = day
@@ -729,22 +856,32 @@ class VipInfo:
for present in self.presentInfo:
pres.append(present.make())
for b in self.vipLoginBonus:
vipBonus.append(b.make())
return [ self.pageYear, self.pageMonth, self.pageDay, self.numItem, pres, vipBonus ]
return [
self.pageYear,
self.pageMonth,
self.pageDay,
self.numItem,
pres,
vipBonus,
]
class PurchaseType(Enum):
PurchaseTypeCredit = 1
PurchaseTypeWP = 2
class PlayType(Enum):
PlayTypeSingle = 1
PlayTypeVs = 2
PlayTypeCoop = 3
PlayTypeStageup = 4
class StageInfo:
danId: int = 0
danLevel: int = 0
@@ -770,15 +907,17 @@ class StageInfo:
self.song2BestScore,
self.song3BestScore,
],
self.unk5
self.unk5,
]
class StageupClearType(Enum):
FAIL = 0
CLEAR_BLUE = 1
CLEAR_SILVER = 2
CLEAR_GOLD = 3
class MusicUpdateDetailV1:
def __init__(self) -> None:
self.songId = 0
@@ -790,7 +929,7 @@ class MusicUpdateDetailV1:
self.lowestMissCount = 0
self.maxSkillPts = 0
self.locked = 0
def make(self) -> List:
return [
self.songId,
@@ -804,25 +943,30 @@ class MusicUpdateDetailV1:
self.locked,
]
class MusicUpdateDetailV2(MusicUpdateDetailV1):
def __init__(self) -> None:
super().__init__()
self.rating = 0
def make(self) -> List:
return super().make() + [self.rating]
class MusicUpdateDetailV3(MusicUpdateDetailV2):
def __init__(self) -> None:
super().__init__()
self.grades = SongDetailGradeCountsV2()
class SongRatingUpdate:
def __init__(self, song_id: int = 0, difficulty: int = 1, new_rating: int = 0) -> None:
def __init__(
self, song_id: int = 0, difficulty: int = 1, new_rating: int = 0
) -> None:
self.songId = song_id
self.difficulty = difficulty
self.rating = new_rating
def make(self) -> List:
return [
self.songId,
@@ -830,21 +974,20 @@ class SongRatingUpdate:
self.rating,
]
class GateTutorialFlag:
def __init__(self, tutorial_id: int = 1, flg_watched: bool = False) -> None:
self.tutorialId = tutorial_id
self.flagWatched = flg_watched
def make(self) -> List:
return [
self.tutorialId,
int(self.flagWatched)
]
return [self.tutorialId, int(self.flagWatched)]
class DateUpdate:
def __init__(self, date_id: int = 0, timestamp: int = 0) -> None:
self.id = date_id
self.timestamp = timestamp
def make(self) -> List:
return [self.id, self.timestamp]

View File

@@ -4,6 +4,7 @@ from titles.wacca.handlers.base import BaseRequest, BaseResponse
from titles.wacca.handlers.helpers import HousingInfo
from titles.wacca.const import WaccaConstants
# ---housing/get----
class HousingGetResponse(BaseResponse):
def __init__(self, housingId: int) -> None:
@@ -15,6 +16,7 @@ class HousingGetResponse(BaseResponse):
self.params = [self.housingId, self.regionId]
return super().make()
# ---housing/start----
class HousingStartRequestV1(BaseRequest):
def __init__(self, data: Dict) -> None:
@@ -26,6 +28,7 @@ class HousingStartRequestV1(BaseRequest):
for info in self.params[2]:
self.info.append(HousingInfo(info[0], info[1]))
class HousingStartRequestV2(HousingStartRequestV1):
def __init__(self, data: Dict) -> None:
super(HousingStartRequestV1, self).__init__(data)
@@ -37,20 +40,84 @@ class HousingStartRequestV2(HousingStartRequestV1):
for info in self.params[3]:
self.info.append(HousingInfo(info[0], info[1]))
class HousingStartResponseV1(BaseResponse):
def __init__(self, regionId: WaccaConstants.Region = WaccaConstants.Region.HOKKAIDO, songList: List[int] = []) -> None:
def __init__(
self,
regionId: WaccaConstants.Region = WaccaConstants.Region.HOKKAIDO,
songList: List[int] = [],
) -> None:
super().__init__()
self.regionId = regionId
self.songList = songList # Recomended songs
self.songList = songList # Recomended songs
if not self.songList:
self.songList = [
1269,1007,1270,1002,1020,1003,1008,1211,1018,1092,1056,32,
1260,1230,1258,1251,2212,1264,1125,1037,2001,1272,1126,1119,
1104,1070,1047,1044,1027,1004,1001,24,2068,2062,2021,1275,
1249,1207,1203,1107,1021,1009,9,4,3,23,22,2014,13,1276,1247,
1240,1237,1128,1114,1110,1109,1102,1045,1043,1036,1035,1030,
1023,1015
self.songList = [
1269,
1007,
1270,
1002,
1020,
1003,
1008,
1211,
1018,
1092,
1056,
32,
1260,
1230,
1258,
1251,
2212,
1264,
1125,
1037,
2001,
1272,
1126,
1119,
1104,
1070,
1047,
1044,
1027,
1004,
1001,
24,
2068,
2062,
2021,
1275,
1249,
1207,
1203,
1107,
1021,
1009,
9,
4,
3,
23,
22,
2014,
13,
1276,
1247,
1240,
1237,
1128,
1114,
1110,
1109,
1102,
1045,
1043,
1036,
1035,
1030,
1023,
1015,
]
def make(self) -> Dict:

View File

@@ -3,6 +3,7 @@ from typing import List, Dict
from titles.wacca.handlers.base import BaseRequest, BaseResponse
from titles.wacca.handlers.helpers import UserOption, DateUpdate
# ---user/info/update---
class UserInfoUpdateRequest(BaseRequest):
def __init__(self, data: Dict) -> None:
@@ -16,17 +17,20 @@ class UserInfoUpdateRequest(BaseRequest):
for x in self.params[1]:
self.optsUpdated.append(UserOption(x[0], x[1]))
for x in self.params[3]:
self.datesUpdated.append(DateUpdate(x[0], x[1]))
# ---user/info/getMyroom--- TODO: Understand this better
class UserInfogetMyroomRequest(BaseRequest):
game_id = 0
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.game_id = int(self.params[0])
class UserInfogetMyroomResponseV1(BaseResponse):
def __init__(self) -> None:
super().__init__()
@@ -49,6 +53,7 @@ class UserInfogetMyroomResponseV1(BaseResponse):
return super().make()
class UserInfogetMyroomResponseV2(UserInfogetMyroomResponseV1):
def __init__(self) -> None:
super().__init__()
@@ -58,13 +63,16 @@ class UserInfogetMyroomResponseV2(UserInfogetMyroomResponseV1):
self.params += [0, 0, 0]
return super(UserInfogetMyroomResponseV1, self).make()
# ---user/info/getRanking---
class UserInfogetRankingRequest(BaseRequest):
game_id = 0
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.game_id = int(self.params[0])
class UserInfogetRankingResponse(BaseResponse):
def __init__(self) -> None:
super().__init__()
@@ -85,4 +93,4 @@ class UserInfogetRankingResponse(BaseResponse):
self.wacca_points_ranking,
]
return super().make()
return super().make()

View File

@@ -5,6 +5,7 @@ from titles.wacca.handlers.helpers import PurchaseType, GenericItemRecv
from titles.wacca.handlers.helpers import TicketItem, SongRatingUpdate, BingoDetail
from titles.wacca.handlers.helpers import BingoPageStatus, GateTutorialFlag
# ---user/goods/purchase---
class UserGoodsPurchaseRequest(BaseRequest):
def __init__(self, data: Dict) -> None:
@@ -14,14 +15,17 @@ class UserGoodsPurchaseRequest(BaseRequest):
self.purchaseCount = int(self.params[2])
self.purchaseType = PurchaseType(self.params[3])
self.cost = int(self.params[4])
self.itemObtained: GenericItemRecv = GenericItemRecv(self.params[5][0], self.params[5][1], self.params[5][2])
self.itemObtained: GenericItemRecv = GenericItemRecv(
self.params[5][0], self.params[5][1], self.params[5][2]
)
class UserGoodsPurchaseResponse(BaseResponse):
def __init__(self, wp: int = 0, tickets: List = []) -> None:
super().__init__()
self.currentWp = wp
self.tickets: List[TicketItem] = []
for ticket in tickets:
self.tickets.append(TicketItem(ticket[0], ticket[1], ticket[2]))
@@ -34,6 +38,7 @@ class UserGoodsPurchaseResponse(BaseResponse):
return super().make()
# ---user/sugaroku/update---
class UserSugarokuUpdateRequestV1(BaseRequest):
def __init__(self, data: Dict) -> None:
@@ -44,17 +49,19 @@ class UserSugarokuUpdateRequestV1(BaseRequest):
self.progress = int(self.params[3])
self.loops = int(self.params[4])
self.boostsUsed = self.params[5]
self.totalPts = int(self.params[7])
self.totalPts = int(self.params[7])
self.itemsObtainted: List[GenericItemRecv] = []
for item in self.params[6]:
self.itemsObtainted.append(GenericItemRecv(item[0], item[1], item[2]))
class UserSugarokuUpdateRequestV2(UserSugarokuUpdateRequestV1):
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.mission_flag = int(self.params[8])
# ---user/rating/update---
class UserRatingUpdateRequest(BaseRequest):
def __init__(self, data: Dict) -> None:
@@ -66,8 +73,9 @@ class UserRatingUpdateRequest(BaseRequest):
for x in self.params[2]:
self.songs.append(SongRatingUpdate(x[0], x[1], x[2]))
# ---user/mission/update---
class UserMissionUpdateRequest(BaseRequest):
class UserMissionUpdateRequest(BaseRequest):
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.profileId = self.params[0]

View File

@@ -1,11 +1,20 @@
from typing import List, Dict
from titles.wacca.handlers.base import BaseRequest, BaseResponse
from titles.wacca.handlers.helpers import GenericItemRecv, SongUpdateDetailV2, TicketItem
from titles.wacca.handlers.helpers import (
GenericItemRecv,
SongUpdateDetailV2,
TicketItem,
)
from titles.wacca.handlers.helpers import MusicUpdateDetailV2, MusicUpdateDetailV3
from titles.wacca.handlers.helpers import SeasonalInfoV2, SeasonalInfoV1, SongUpdateDetailV1
from titles.wacca.handlers.helpers import (
SeasonalInfoV2,
SeasonalInfoV1,
SongUpdateDetailV1,
)
from titles.wacca.handlers.helpers import MusicUpdateDetailV1
# ---user/music/update---
class UserMusicUpdateRequestV1(BaseRequest):
def __init__(self, data: Dict) -> None:
@@ -18,82 +27,86 @@ class UserMusicUpdateRequestV1(BaseRequest):
for itm in data["params"][3]:
self.itemsObtained.append(GenericItemRecv(itm[0], itm[1], itm[2]))
class UserMusicUpdateRequestV2(UserMusicUpdateRequestV1):
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.songDetail = SongUpdateDetailV2(self.params[2])
class UserMusicUpdateResponseV1(BaseResponse):
def __init__(self) -> None:
super().__init__()
self.songDetail = MusicUpdateDetailV1()
self.seasonInfo = SeasonalInfoV1()
self.rankingInfo: List[List[int]] = []
def make(self) -> Dict:
self.params = [
self.songDetail.make(),
[self.songDetail.songId, self.songDetail.clearCounts.playCt],
self.seasonInfo.make(),
self.rankingInfo
self.rankingInfo,
]
return super().make()
class UserMusicUpdateResponseV2(UserMusicUpdateResponseV1):
def __init__(self) -> None:
super().__init__()
self.songDetail = MusicUpdateDetailV2()
self.seasonInfo = SeasonalInfoV2()
class UserMusicUpdateResponseV3(UserMusicUpdateResponseV2):
def __init__(self) -> None:
super().__init__()
self.songDetail = MusicUpdateDetailV3()
# ---user/music/updateCoop---
class UserMusicUpdateCoopRequest(UserMusicUpdateRequestV2):
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.coopData = self.params[4]
# ---user/music/updateVs---
class UserMusicUpdateVsRequest(UserMusicUpdateRequestV2):
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.vsData = self.params[4]
# ---user/music/unlock---
class UserMusicUnlockRequest(BaseRequest):
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.profileId = self.params[0]
self.songId = self.params[1]
self.difficulty = self.params[2]
self.difficulty = self.params[2]
self.itemsUsed: List[GenericItemRecv] = []
for itm in self.params[3]:
self.itemsUsed.append(GenericItemRecv(itm[0], itm[1], itm[2]))
class UserMusicUnlockResponse(BaseResponse):
def __init__(self, current_wp: int = 0, tickets_remaining: List = []) -> None:
super().__init__()
self.wp = current_wp
self.wp = current_wp
self.tickets: List[TicketItem] = []
for ticket in tickets_remaining:
self.tickets.append(TicketItem(ticket[0], ticket[1], ticket[2]))
def make(self)-> Dict:
def make(self) -> Dict:
tickets = []
for ticket in self.tickets:
tickets.append(ticket.make())
self.params = [
self.wp,
tickets
]
self.params = [self.wp, tickets]
return super().make()

View File

@@ -3,6 +3,7 @@ from typing import List, Dict, Optional
from titles.wacca.handlers.base import BaseRequest, BaseResponse
from titles.wacca.handlers.helpers import *
# ---user/status/get----
class UserStatusGetRequest(BaseRequest):
aimeId: int = 0
@@ -11,6 +12,7 @@ class UserStatusGetRequest(BaseRequest):
super().__init__(data)
self.aimeId = int(data["params"][0])
class UserStatusGetV1Response(BaseResponse):
def __init__(self) -> None:
super().__init__()
@@ -27,14 +29,12 @@ class UserStatusGetV1Response(BaseResponse):
self.setTitleId,
self.setIconId,
self.profileStatus.value,
[
self.versionStatus.value,
str(self.lastGameVersion)
]
[self.versionStatus.value, str(self.lastGameVersion)],
]
return super().make()
class UserStatusGetV2Response(UserStatusGetV1Response):
def __init__(self) -> None:
super().__init__()
@@ -48,6 +48,7 @@ class UserStatusGetV2Response(UserStatusGetV1Response):
return super(UserStatusGetV1Response, self).make()
# ---user/status/getDetail----
class UserStatusGetDetailRequest(BaseRequest):
userId: int = 0
@@ -56,6 +57,7 @@ class UserStatusGetDetailRequest(BaseRequest):
super().__init__(data)
self.userId = data["params"][0]
class UserStatusGetDetailResponseV1(BaseResponse):
def __init__(self) -> None:
super().__init__()
@@ -64,22 +66,32 @@ class UserStatusGetDetailResponseV1(BaseResponse):
self.seasonalPlayModeCounts: List[PlayModeCounts] = []
self.userItems: UserItemInfoV1 = UserItemInfoV1()
self.scores: List[BestScoreDetailV1] = []
self.songPlayStatus: List[int] = [0,0]
self.songPlayStatus: List[int] = [0, 0]
self.seasonInfo: SeasonalInfoV1 = SeasonalInfoV1()
self.playAreaList: List = [ [0],[0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0],[0,0,0,0],[0,0,0,0,0,0,0],[0] ]
self.playAreaList: List = [
[0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0],
]
self.songUpdateTime: int = 0
def make(self)-> Dict:
def make(self) -> Dict:
opts = []
play_modes = []
scores = []
for x in self.seasonalPlayModeCounts:
play_modes.append(x.make())
for x in self.scores:
scores.append(x.make())
for x in self.options:
opts.append(x.make())
@@ -92,21 +104,31 @@ class UserStatusGetDetailResponseV1(BaseResponse):
self.songPlayStatus,
self.seasonInfo.make(),
self.playAreaList,
self.songUpdateTime
self.songUpdateTime,
]
return super().make()
def find_score_idx(self, song_id: int, difficulty: int = 1, start_idx: int = 0, stop_idx: Optional[int] = None) -> Optional[int]:
def find_score_idx(
self,
song_id: int,
difficulty: int = 1,
start_idx: int = 0,
stop_idx: Optional[int] = None,
) -> Optional[int]:
if stop_idx is None or stop_idx > len(self.scores):
stop_idx = len(self.scores)
for x in range(start_idx, stop_idx):
if self.scores[x].songId == song_id and self.scores[x].difficulty == difficulty:
if (
self.scores[x].songId == song_id
and self.scores[x].difficulty == difficulty
):
return x
return None
class UserStatusGetDetailResponseV2(UserStatusGetDetailResponseV1):
def __init__(self) -> None:
super().__init__()
@@ -122,7 +144,7 @@ class UserStatusGetDetailResponseV2(UserStatusGetDetailResponseV1):
self.gatchaInfo: List[GachaInfo] = []
self.friendList: List[FriendDetail] = []
def make(self)-> Dict:
def make(self) -> Dict:
super().make()
gates = []
friends = []
@@ -130,13 +152,13 @@ class UserStatusGetDetailResponseV2(UserStatusGetDetailResponseV1):
for x in self.gateInfo:
gates.append(x.make())
for x in self.friendList:
friends.append(x.make())
for x in self.gateTutorialFlags:
tut_flg.append(x.make())
while len(tut_flg) < 5:
flag_id = len(tut_flg) + 1
tut_flg.append([flag_id, 0])
@@ -152,11 +174,13 @@ class UserStatusGetDetailResponseV2(UserStatusGetDetailResponseV1):
return super(UserStatusGetDetailResponseV1, self).make()
class UserStatusGetDetailResponseV3(UserStatusGetDetailResponseV2):
def __init__(self) -> None:
super().__init__()
self.gateInfo: List[GateDetailV2] = []
class UserStatusGetDetailResponseV4(UserStatusGetDetailResponseV3):
def __init__(self) -> None:
super().__init__()
@@ -164,12 +188,13 @@ class UserStatusGetDetailResponseV4(UserStatusGetDetailResponseV3):
self.bingoStatus: BingoDetail = BingoDetail(0)
self.scores: List[BestScoreDetailV2] = []
def make(self)-> Dict:
def make(self) -> Dict:
super().make()
self.params.append(self.bingoStatus.make())
return super(UserStatusGetDetailResponseV1, self).make()
# ---user/status/login----
class UserStatusLoginRequest(BaseRequest):
userId: int = 0
@@ -178,16 +203,19 @@ class UserStatusLoginRequest(BaseRequest):
super().__init__(data)
self.userId = data["params"][0]
class UserStatusLoginResponseV1(BaseResponse):
def __init__(self, is_first_login_daily: bool = False, last_login_date: int = 0) -> None:
def __init__(
self, is_first_login_daily: bool = False, last_login_date: int = 0
) -> None:
super().__init__()
self.dailyBonus: List[LoginBonusInfo] = []
self.consecBonus: List[LoginBonusInfo] = []
self.otherBonus: List[LoginBonusInfo] = []
self.otherBonus: List[LoginBonusInfo] = []
self.firstLoginDaily = is_first_login_daily
self.lastLoginDate = last_login_date
def make(self)-> Dict:
def make(self) -> Dict:
super().make()
daily = []
consec = []
@@ -202,32 +230,39 @@ class UserStatusLoginResponseV1(BaseResponse):
for bonus in self.otherBonus:
other.append(bonus.make())
self.params = [ daily, consec, other, int(self.firstLoginDaily)]
self.params = [daily, consec, other, int(self.firstLoginDaily)]
return super().make()
class UserStatusLoginResponseV2(UserStatusLoginResponseV1):
def __init__(self, is_first_login_daily: bool = False, last_login_date: int = 0) -> None:
def __init__(
self, is_first_login_daily: bool = False, last_login_date: int = 0
) -> None:
super().__init__(is_first_login_daily)
self.lastLoginDate = last_login_date
self.vipInfo = VipInfo()
def make(self)-> Dict:
def make(self) -> Dict:
super().make()
self.params.append(self.vipInfo.make())
self.params.append(self.lastLoginDate)
return super(UserStatusLoginResponseV1, self).make()
class UserStatusLoginResponseV3(UserStatusLoginResponseV2):
def __init__(self, is_first_login_daily: bool = False, last_login_date: int = 0) -> None:
def __init__(
self, is_first_login_daily: bool = False, last_login_date: int = 0
) -> None:
super().__init__(is_first_login_daily, last_login_date)
self.unk: List = []
def make(self)-> Dict:
def make(self) -> Dict:
super().make()
self.params.append(self.unk)
return super(UserStatusLoginResponseV1, self).make()
# ---user/status/create---
class UserStatusCreateRequest(BaseRequest):
def __init__(self, data: Dict) -> None:
@@ -235,26 +270,27 @@ class UserStatusCreateRequest(BaseRequest):
self.aimeId = data["params"][0]
self.username = data["params"][1]
class UserStatusCreateResponseV1(BaseResponse):
def __init__(self, userId: int, username: str) -> None:
super().__init__()
self.userStatus = UserStatusV1()
self.userStatus.userId = userId
self.userStatus.username = username
def make(self)-> Dict:
self.params = [
self.userStatus.make()
]
def make(self) -> Dict:
self.params = [self.userStatus.make()]
return super().make()
class UserStatusCreateResponseV2(UserStatusCreateResponseV1):
def __init__(self, userId: int, username: str) -> None:
super().__init__(userId, username)
self.userStatus: UserStatusV2 = UserStatusV2()
self.userStatus: UserStatusV2 = UserStatusV2()
self.userStatus.userId = userId
self.userStatus.username = username
# ---user/status/logout---
class UserStatusLogoutRequest(BaseRequest):
userId: int
@@ -263,6 +299,7 @@ class UserStatusLogoutRequest(BaseRequest):
super().__init__(data)
self.userId = data["params"][0]
# ---user/status/update---
class UserStatusUpdateRequestV1(BaseRequest):
def __init__(self, data: Dict) -> None:
@@ -274,11 +311,17 @@ class UserStatusUpdateRequestV1(BaseRequest):
for itm in data["params"][2]:
self.itemsRecieved.append(GenericItemRecv(itm[0], itm[1], itm[2]))
class UserStatusUpdateRequestV2(UserStatusUpdateRequestV1):
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.isContinue = bool(data["params"][3])
self.isFirstPlayFree = bool(data["params"][4])
self.itemsUsed = data["params"][5]
self.lastSongInfo = LastSongDetail(data["params"][6][0], data["params"][6][1],
data["params"][6][2], data["params"][6][3], data["params"][6][4])
self.lastSongInfo = LastSongDetail(
data["params"][6][0],
data["params"][6][1],
data["params"][6][2],
data["params"][6][3],
data["params"][6][4],
)

View File

@@ -2,6 +2,7 @@ from typing import Dict, List
from titles.wacca.handlers.base import BaseRequest, BaseResponse
from titles.wacca.handlers.helpers import StageInfo, StageupClearType, GenericItemRecv
# --user/trial/get--
class UserTrialGetRequest(BaseRequest):
profileId: int = 0
@@ -10,20 +11,22 @@ class UserTrialGetRequest(BaseRequest):
super().__init__(data)
self.profileId = self.params[0]
class UserTrialGetResponse(BaseResponse):
def __init__(self) -> None:
super().__init__()
self.stageList: List[StageInfo] = []
def make(self) -> Dict:
dans = []
for x in self.stageList:
dans.append(x.make())
self.params = [dans]
return super().make()
# --user/trial/update--
class UserTrialUpdateRequest(BaseRequest):
def __init__(self, data: Dict) -> None:
@@ -43,9 +46,10 @@ class UserTrialUpdateRequest(BaseRequest):
if len(self.params) == 8:
self.unk7 = self.params[7]
class UserTrialUpdateResponse(BaseResponse):
def __init__(self) -> None:
super().__init__()
def make(self) -> Dict:
return super().make()
return super().make()

View File

@@ -2,12 +2,14 @@ from typing import Dict, List
from titles.wacca.handlers.base import BaseRequest, BaseResponse
from titles.wacca.handlers.helpers import VipLoginBonus
# --user/vip/get--
class UserVipGetRequest(BaseRequest):
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.profileId = self.params[0]
class UserVipGetResponse(BaseResponse):
def __init__(self) -> None:
super().__init__()
@@ -15,22 +17,16 @@ class UserVipGetResponse(BaseResponse):
self.unknown1: int = 1
self.unknown2: int = 1
self.presents: List[VipLoginBonus] = []
def make(self) -> Dict:
pres = []
for x in self.presents:
pres.append(x.make())
self.params = [
self.vipDays,
[
self.unknown1,
self.unknown2,
pres
]
]
self.params = [self.vipDays, [self.unknown1, self.unknown2, pres]]
return super().make()
# --user/vip/start--
class UserVipStartRequest(BaseRequest):
def __init__(self, data: Dict) -> None:
@@ -39,6 +35,7 @@ class UserVipStartRequest(BaseRequest):
self.cost = self.params[1]
self.days = self.params[2]
class UserVipStartResponse(BaseResponse):
def __init__(self, expires: int = 0) -> None:
super().__init__()
@@ -46,9 +43,6 @@ class UserVipStartResponse(BaseResponse):
self.presents = []
def make(self) -> Dict:
self.params = [
self.whenExpires,
self.presents
]
self.params = [self.whenExpires, self.presents]
return super().make()
return super().make()

View File

@@ -20,12 +20,15 @@ from titles.wacca.base import WaccaBase
from titles.wacca.handlers.base import BaseResponse
from titles.wacca.handlers.helpers import Version
class WaccaServlet():
class WaccaServlet:
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
self.core_cfg = core_cfg
self.game_cfg = WaccaConfig()
if path.exists(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}"):
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}")))
self.game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}"))
)
self.versions = [
WaccaBase(core_cfg, self.game_cfg),
@@ -38,32 +41,46 @@ class WaccaServlet():
self.logger = logging.getLogger("wacca")
log_fmt_str = "[%(asctime)s] Wacca | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str)
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "wacca"), encoding='utf8',
when="d", backupCount=10)
fileHandler = TimedRotatingFileHandler(
"{0}/{1}.log".format(self.core_cfg.server.log_dir, "wacca"),
encoding="utf8",
when="d",
backupCount=10,
)
fileHandler.setFormatter(log_fmt)
consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(log_fmt)
self.logger.addHandler(fileHandler)
self.logger.addHandler(consoleHandler)
self.logger.setLevel(self.game_cfg.server.loglevel)
coloredlogs.install(level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str)
coloredlogs.install(
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
)
@classmethod
def get_allnet_info(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str, str]:
def get_allnet_info(
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
) -> Tuple[bool, str, str]:
game_cfg = WaccaConfig()
if path.exists(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}"):
game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}")))
game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}"))
)
if not game_cfg.server.enable:
return (False, "", "")
if core_cfg.server.is_develop:
return (True, f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v", "")
return (
True,
f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v",
"",
)
return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v", "")
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
@@ -71,12 +88,14 @@ class WaccaServlet():
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()
try:
req_json = json.loads(request.content.getvalue())
version_full = Version(req_json["appVersion"])
except:
self.logger.error(f"Failed to parse request toi {request.uri} -> {request.content.getvalue()}")
self.logger.error(
f"Failed to parse request toi {request.uri} -> {request.content.getvalue()}"
)
resp = BaseResponse()
resp.status = 1
resp.message = "不正なリクエスト エラーです"
@@ -94,7 +113,7 @@ class WaccaServlet():
if ver_search < 15000:
internal_ver = WaccaConstants.VER_WACCA
elif ver_search >= 15000 and ver_search < 20000:
internal_ver = WaccaConstants.VER_WACCA_S
@@ -103,37 +122,45 @@ class WaccaServlet():
elif ver_search >= 25000 and ver_search < 30000:
internal_ver = WaccaConstants.VER_WACCA_LILY_R
elif ver_search >= 30000:
internal_ver = WaccaConstants.VER_WACCA_REVERSE
else:
self.logger.warning(f"Unsupported version ({req_json['appVersion']}) request {url_path} - {req_json}")
self.logger.warning(
f"Unsupported version ({req_json['appVersion']}) request {url_path} - {req_json}"
)
resp = BaseResponse()
resp.status = 1
resp.message = "不正なアプリバージョンエラーです"
return end(resp.make())
self.logger.info(f"v{req_json['appVersion']} {url_path} request from {request.getClientAddress().host} with chipId {req_json['chipId']}")
self.logger.info(
f"v{req_json['appVersion']} {url_path} request from {request.getClientAddress().host} with chipId {req_json['chipId']}"
)
self.logger.debug(req_json)
if not hasattr(self.versions[internal_ver], func_to_find):
self.logger.warn(f"{req_json['appVersion']} has no handler for {func_to_find}")
self.logger.warn(
f"{req_json['appVersion']} has no handler for {func_to_find}"
)
resp = BaseResponse().make()
return end(resp)
try:
handler = getattr(self.versions[internal_ver], func_to_find)
resp = handler(req_json)
self.logger.debug(f"{req_json['appVersion']} response {resp}")
return end(resp)
except Exception as e:
self.logger.error(f"{req_json['appVersion']} Error handling method {url_path} -> {e}")
self.logger.error(
f"{req_json['appVersion']} Error handling method {url_path} -> {e}"
)
if self.core_cfg.server.is_develop:
raise
resp = BaseResponse()
resp.status = 1
resp.message = "A server error occoured."

View File

@@ -9,6 +9,7 @@ from titles.wacca.const import WaccaConstants
from titles.wacca.handlers import *
class WaccaLily(WaccaS):
def __init__(self, cfg: CoreConfig, game_cfg: WaccaConfig) -> None:
super().__init__(cfg, game_cfg)
@@ -35,31 +36,35 @@ class WaccaLily(WaccaS):
(210002, 0),
(210003, 0),
]
def handle_advertise_GetNews_request(self, data: Dict)-> Dict:
def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
resp = GetNewsResponseV3()
return resp.make()
def handle_housing_start_request(self, data: Dict) -> Dict:
req = HousingStartRequestV2(data)
if req.appVersion.country != "JPN" and req.appVersion.country in [region.name for region in WaccaConstants.Region]:
if req.appVersion.country != "JPN" and req.appVersion.country in [
region.name for region in WaccaConstants.Region
]:
region_id = WaccaConstants.Region[req.appVersion.country]
else:
region_id = self.region_id
resp = HousingStartResponseV1(region_id)
return resp.make()
def handle_user_status_create_request(self, data: Dict)-> Dict:
def handle_user_status_create_request(self, data: Dict) -> Dict:
req = UserStatusCreateRequest(data)
resp = super().handle_user_status_create_request(data)
self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210002) # Lily, Added Lily
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210002
) # Lily, Added Lily
return resp
def handle_user_status_get_request(self, data: Dict)-> Dict:
def handle_user_status_get_request(self, data: Dict) -> Dict:
req = UserStatusGetRequest(data)
resp = UserStatusGetV2Response()
@@ -74,7 +79,7 @@ class WaccaLily(WaccaS):
resp.lastGameVersion = ShortVersion(str(req.appVersion))
else:
resp.lastGameVersion = ShortVersion(profile["last_game_ver"])
resp.userStatus.userId = profile["id"]
resp.userStatus.username = profile["username"]
resp.userStatus.xp = profile["xp"]
@@ -87,40 +92,55 @@ class WaccaLily(WaccaS):
resp.userStatus.loginsToday = profile["login_count_today"]
resp.userStatus.rating = profile["rating"]
set_title_id = self.data.profile.get_options(WaccaConstants.OPTIONS["set_title_id"], profile["user"])
set_title_id = self.data.profile.get_options(
WaccaConstants.OPTIONS["set_title_id"], profile["user"]
)
if set_title_id is None:
set_title_id = self.OPTIONS_DEFAULTS["set_title_id"]
resp.setTitleId = set_title_id
set_icon_id = self.data.profile.get_options(WaccaConstants.OPTIONS["set_title_id"], profile["user"])
set_icon_id = self.data.profile.get_options(
WaccaConstants.OPTIONS["set_title_id"], profile["user"]
)
if set_icon_id is None:
set_icon_id = self.OPTIONS_DEFAULTS["set_icon_id"]
resp.setIconId = set_icon_id
if profile["last_login_date"].timestamp() < int(datetime.now().replace(hour=0,minute=0,second=0,microsecond=0).timestamp()):
if profile["last_login_date"].timestamp() < int(
datetime.now()
.replace(hour=0, minute=0, second=0, microsecond=0)
.timestamp()
):
resp.userStatus.loginsToday = 0
if profile["last_login_date"].timestamp() < int((datetime.now().replace(hour=0,minute=0,second=0,microsecond=0) - timedelta(days=1)).timestamp()):
if profile["last_login_date"].timestamp() < int(
(
datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
- timedelta(days=1)
).timestamp()
):
resp.userStatus.loginConsecutiveDays = 0
if req.appVersion > resp.lastGameVersion:
resp.versionStatus = PlayVersionStatus.VersionUpgrade
elif req.appVersion < resp.lastGameVersion:
resp.versionStatus = PlayVersionStatus.VersionTooNew
if profile["vip_expire_time"] is not None:
resp.userStatus.vipExpireTime = int(profile["vip_expire_time"].timestamp())
if profile["always_vip"] or self.game_config.mods.always_vip:
resp.userStatus.vipExpireTime = int((datetime.now() + timedelta(days=30)).timestamp())
resp.userStatus.vipExpireTime = int(
(datetime.now() + timedelta(days=30)).timestamp()
)
if self.game_config.mods.infinite_wp:
resp.userStatus.wp = 999999
return resp.make()
def handle_user_status_login_request(self, data: Dict)-> Dict:
def handle_user_status_login_request(self, data: Dict) -> Dict:
req = UserStatusLoginRequest(data)
resp = UserStatusLoginResponseV2()
is_new_day = False
@@ -130,27 +150,38 @@ class WaccaLily(WaccaS):
if req.userId == 0:
self.logger.info(f"Guest login on {req.chipId}")
resp.lastLoginDate = 0
else:
profile = self.data.profile.get_profile(req.userId)
if profile is None:
self.logger.warn(f"Unknown user id {req.userId} attempted login from {req.chipId}")
self.logger.warn(
f"Unknown user id {req.userId} attempted login from {req.chipId}"
)
return resp.make()
self.logger.info(f"User {req.userId} login on {req.chipId}")
last_login_time = int(profile["last_login_date"].timestamp())
resp.lastLoginDate = last_login_time
# If somebodies login timestamp < midnight of current day, then they are logging in for the first time today
if last_login_time < int(datetime.now().replace(hour=0,minute=0,second=0,microsecond=0).timestamp()):
# If somebodies login timestamp < midnight of current day, then they are logging in for the first time today
if last_login_time < int(
datetime.now()
.replace(hour=0, minute=0, second=0, microsecond=0)
.timestamp()
):
is_new_day = True
is_consec_day = True
# If somebodies login timestamp > midnight of current day + 1 day, then they broke their daily login streak
elif last_login_time > int((datetime.now().replace(hour=0,minute=0,second=0,microsecond=0) + timedelta(days=1)).timestamp()):
elif last_login_time > int(
(
datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
+ timedelta(days=1)
).timestamp()
):
is_consec_day = False
# else, they are simply logging in again on the same day, and we don't need to do anything for that
self.data.profile.session_login(req.userId, is_new_day, is_consec_day)
resp.vipInfo.pageYear = datetime.now().year
resp.vipInfo.pageMonth = datetime.now().month
@@ -158,10 +189,10 @@ class WaccaLily(WaccaS):
resp.vipInfo.numItem = 1
resp.firstLoginDaily = int(is_new_day)
return resp.make()
def handle_user_status_getDetail_request(self, data: Dict)-> Dict:
def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
req = UserStatusGetDetailRequest(data)
if req.appVersion.minor >= 53:
resp = UserStatusGetDetailResponseV3()
@@ -187,15 +218,23 @@ class WaccaLily(WaccaS):
if profile["vip_expire_time"] is None:
resp.userStatus.vipExpireTime = 0
else:
resp.userStatus.vipExpireTime = int(profile["vip_expire_time"].timestamp())
resp.userStatus.vipExpireTime = int(profile["vip_expire_time"].timestamp())
if profile["always_vip"] or self.game_config.mods.always_vip:
resp.userStatus.vipExpireTime = int((self.srvtime + timedelta(days=31)).timestamp())
resp.userStatus.vipExpireTime = int(
(self.srvtime + timedelta(days=31)).timestamp()
)
resp.songUpdateTime = int(profile["last_login_date"].timestamp())
resp.lastSongInfo = LastSongDetail(profile["last_song_id"],profile["last_song_difficulty"],profile["last_folder_order"],profile["last_folder_id"],profile["last_song_order"])
resp.lastSongInfo = LastSongDetail(
profile["last_song_id"],
profile["last_song_difficulty"],
profile["last_folder_order"],
profile["last_folder_id"],
profile["last_song_order"],
)
resp.songPlayStatus = [resp.lastSongInfo.lastSongId, 1]
resp.userStatus.userId = profile["id"]
@@ -208,41 +247,65 @@ class WaccaLily(WaccaS):
resp.userStatus.loginDays = profile["login_count_days"]
resp.userStatus.loginConsecutiveDays = profile["login_count_days_consec"]
resp.userStatus.loginsToday = profile["login_count_today"]
resp.userStatus.rating = profile['rating']
resp.userStatus.rating = profile["rating"]
if self.game_config.mods.infinite_wp:
resp.userStatus.wp = 999999
for fav in profile_favorites:
resp.favorites.append(fav["song_id"])
if profile["friend_view_1"] is not None:
pass
if profile["friend_view_2"] is not None:
pass
if profile["friend_view_3"] is not None:
pass
resp.seasonalPlayModeCounts.append(PlayModeCounts(self.season, 1, profile["playcount_single"]))
resp.seasonalPlayModeCounts.append(PlayModeCounts(self.season, 2, profile["playcount_multi_vs"]))
resp.seasonalPlayModeCounts.append(PlayModeCounts(self.season, 3, profile["playcount_multi_coop"]))
resp.seasonalPlayModeCounts.append(PlayModeCounts(self.season, 4, profile["playcount_stageup"]))
resp.seasonalPlayModeCounts.append(
PlayModeCounts(self.season, 1, profile["playcount_single"])
)
resp.seasonalPlayModeCounts.append(
PlayModeCounts(self.season, 2, profile["playcount_multi_vs"])
)
resp.seasonalPlayModeCounts.append(
PlayModeCounts(self.season, 3, profile["playcount_multi_coop"])
)
resp.seasonalPlayModeCounts.append(
PlayModeCounts(self.season, 4, profile["playcount_stageup"])
)
for opt in profile_options:
resp.options.append(UserOption(opt["opt_id"], opt["value"]))
for gate in self.game_config.gates.enabled_gates:
added_gate = False
for user_gate in profile_gates:
if user_gate["gate_id"] == gate:
if req.appVersion.minor >= 53:
resp.gateInfo.append(GateDetailV2(user_gate["gate_id"],user_gate["page"],user_gate["progress"],
user_gate["loops"],int(user_gate["last_used"].timestamp()),user_gate["mission_flag"]))
resp.gateInfo.append(
GateDetailV2(
user_gate["gate_id"],
user_gate["page"],
user_gate["progress"],
user_gate["loops"],
int(user_gate["last_used"].timestamp()),
user_gate["mission_flag"],
)
)
else:
resp.gateInfo.append(GateDetailV1(user_gate["gate_id"],user_gate["page"],user_gate["progress"],
user_gate["loops"],int(user_gate["last_used"].timestamp()),user_gate["mission_flag"]))
resp.gateInfo.append(
GateDetailV1(
user_gate["gate_id"],
user_gate["page"],
user_gate["progress"],
user_gate["loops"],
int(user_gate["last_used"].timestamp()),
user_gate["mission_flag"],
)
)
resp.seasonInfo.cumulativeGatePts += user_gate["total_points"]
@@ -252,17 +315,21 @@ class WaccaLily(WaccaS):
if not added_gate:
if req.appVersion.minor >= 53:
resp.gateInfo.append(GateDetailV2(gate))
else:
resp.gateInfo.append(GateDetailV1(gate))
for unlock in profile_song_unlocks:
for x in range(1, unlock["highest_difficulty"] + 1):
resp.userItems.songUnlocks.append(SongUnlock(unlock["song_id"], x, 0, int(unlock["acquire_date"].timestamp())))
resp.userItems.songUnlocks.append(
SongUnlock(
unlock["song_id"], x, 0, int(unlock["acquire_date"].timestamp())
)
)
for song in profile_scores:
resp.seasonInfo.cumulativeScore += song["score"]
clear_cts = SongDetailClearCounts(
song["play_ct"],
song["clear_ct"],
@@ -272,13 +339,20 @@ class WaccaLily(WaccaS):
)
grade_cts = SongDetailGradeCountsV1(
song["grade_d_ct"], song["grade_c_ct"], song["grade_b_ct"], song["grade_a_ct"], song["grade_aa_ct"],
song["grade_aaa_ct"], song["grade_s_ct"], song["grade_ss_ct"], song["grade_sss_ct"],
song["grade_master_ct"]
song["grade_d_ct"],
song["grade_c_ct"],
song["grade_b_ct"],
song["grade_a_ct"],
song["grade_aa_ct"],
song["grade_aaa_ct"],
song["grade_s_ct"],
song["grade_ss_ct"],
song["grade_sss_ct"],
song["grade_master_ct"],
)
deets = BestScoreDetailV1(song["song_id"], song["chart_id"])
deets.clearCounts = clear_cts
deets.clearCounts = clear_cts
deets.clearCountsSeason = clear_cts
deets.gradeCounts = grade_cts
deets.score = song["score"]
@@ -287,9 +361,16 @@ class WaccaLily(WaccaS):
deets.rating = song["rating"]
resp.scores.append(deets)
for trophy in profile_trophies:
resp.userItems.trophies.append(TrophyItem(trophy["trophy_id"], trophy["season"], trophy["progress"], trophy["badge_type"]))
resp.userItems.trophies.append(
TrophyItem(
trophy["trophy_id"],
trophy["season"],
trophy["progress"],
trophy["badge_type"],
)
)
if self.game_config.mods.infinite_tickets:
for x in range(5):
@@ -301,27 +382,45 @@ class WaccaLily(WaccaS):
else:
expire = int(ticket["expire_date"].timestamp())
resp.userItems.tickets.append(TicketItem(ticket["id"], ticket["ticket_id"], expire))
resp.userItems.tickets.append(
TicketItem(ticket["id"], ticket["ticket_id"], expire)
)
if profile_items:
for item in profile_items:
try:
if item["type"] == WaccaConstants.ITEM_TYPES["icon"]:
resp.userItems.icons.append(IconItem(item["item_id"], 1, item["use_count"], int(item["acquire_date"].timestamp())))
resp.userItems.icons.append(
IconItem(
item["item_id"],
1,
item["use_count"],
int(item["acquire_date"].timestamp()),
)
)
elif item["type"] == WaccaConstants.ITEM_TYPES["navigator"]:
resp.userItems.navigators.append(NavigatorItem(item["item_id"], 1, int(item["acquire_date"].timestamp()), item["use_count"], item["use_count"]))
resp.userItems.navigators.append(
NavigatorItem(
item["item_id"],
1,
int(item["acquire_date"].timestamp()),
item["use_count"],
item["use_count"],
)
)
else:
itm_send = GenericItemSend(item["item_id"], 1, int(item["acquire_date"].timestamp()))
itm_send = GenericItemSend(
item["item_id"], 1, int(item["acquire_date"].timestamp())
)
if item["type"] == WaccaConstants.ITEM_TYPES["title"]:
resp.userItems.titles.append(itm_send)
elif item["type"] == WaccaConstants.ITEM_TYPES["user_plate"]:
resp.userItems.plates.append(itm_send)
elif item["type"] == WaccaConstants.ITEM_TYPES["note_color"]:
resp.userItems.noteColors.append(itm_send)
@@ -329,7 +428,9 @@ class WaccaLily(WaccaS):
resp.userItems.noteSounds.append(itm_send)
except:
self.logger.error(f"{__name__} Failed to load item {item['item_id']} for user {profile['user']}")
self.logger.error(
f"{__name__} Failed to load item {item['item_id']} for user {profile['user']}"
)
resp.seasonInfo.level = profile["xp"]
resp.seasonInfo.wpObtained = profile["wp_total"]
@@ -342,12 +443,18 @@ class WaccaLily(WaccaS):
return resp.make()
def handle_user_info_getMyroom_request(self, data: Dict)-> Dict:
def handle_user_info_getMyroom_request(self, data: Dict) -> Dict:
return UserInfogetMyroomResponseV2().make()
def handle_user_status_update_request(self, data: Dict)-> Dict:
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(req.profileId, req.lastSongInfo.lastSongId, req.lastSongInfo.lastSongDiff,
req.lastSongInfo.lastFolderOrd, req.lastSongInfo.lastFolderId, req.lastSongInfo.lastSongOrd)
return BaseResponse().make()
req = UserStatusUpdateRequestV2(data)
self.data.profile.update_profile_lastplayed(
req.profileId,
req.lastSongInfo.lastSongId,
req.lastSongInfo.lastSongDiff,
req.lastSongInfo.lastFolderOrd,
req.lastSongInfo.lastFolderId,
req.lastSongInfo.lastSongOrd,
)
return BaseResponse().make()

View File

@@ -8,6 +8,7 @@ from titles.wacca.config import WaccaConfig
from titles.wacca.const import WaccaConstants
from titles.wacca.handlers import *
class WaccaLilyR(WaccaLily):
def __init__(self, cfg: CoreConfig, game_cfg: WaccaConfig) -> None:
super().__init__(cfg, game_cfg)
@@ -35,20 +36,36 @@ class WaccaLilyR(WaccaLily):
(210003, 0),
]
def handle_user_status_create_request(self, data: Dict)-> Dict:
def handle_user_status_create_request(self, data: Dict) -> Dict:
req = UserStatusCreateRequest(data)
resp = super().handle_user_status_create_request(data)
self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210054) # Added lily r
self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210055) # Added lily r
self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210056) # Added lily r
self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210057) # Added lily r
self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210058) # Added lily r
self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210059) # Added lily r
self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210060) # Added lily r
self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210061) # Added lily r
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210054
) # Added lily r
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210055
) # Added lily r
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210056
) # Added lily r
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210057
) # Added lily r
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210058
) # Added lily r
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210059
) # Added lily r
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210060
) # Added lily r
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210061
) # Added lily r
return resp
def handle_user_status_logout_request(self, data: Dict)-> Dict:
def handle_user_status_logout_request(self, data: Dict) -> Dict:
return BaseResponse().make()

View File

@@ -8,42 +8,57 @@ from core.config import CoreConfig
from titles.wacca.database import WaccaData
from titles.wacca.const import WaccaConstants
class WaccaReader(BaseReader):
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str], opt_dir: Optional[str], extra: Optional[str]) -> None:
def __init__(
self,
config: CoreConfig,
version: int,
bin_dir: Optional[str],
opt_dir: Optional[str],
extra: Optional[str],
) -> None:
super().__init__(config, version, bin_dir, opt_dir, extra)
self.data = WaccaData(config)
try:
self.logger.info(f"Start importer for {WaccaConstants.game_ver_to_string(version)}")
self.logger.info(
f"Start importer for {WaccaConstants.game_ver_to_string(version)}"
)
except IndexError:
self.logger.error(f"Invalid wacca version {version}")
exit(1)
def read(self) -> None:
if not (path.exists(f"{self.bin_dir}/Table") and path.exists(f"{self.bin_dir}/Message")):
if not (
path.exists(f"{self.bin_dir}/Table")
and path.exists(f"{self.bin_dir}/Message")
):
self.logger.error("Could not find Table or Message folder, nothing to read")
return
self.read_music(f"{self.bin_dir}/Table", "MusicParameterTable")
def read_music(self, base_dir: str, table: str) -> None:
if not self.check_valid_pair(base_dir, table):
self.logger.warn(f"Cannot find {table} uasset/uexp pair at {base_dir}, music will not be read")
self.logger.warn(
f"Cannot find {table} uasset/uexp pair at {base_dir}, music will not be read"
)
return
uasset=open(f"{base_dir}/{table}.uasset", "rb")
uexp=open(f"{base_dir}/{table}.uexp", "rb")
package = wacky.jsonify(uasset,uexp)
uasset = open(f"{base_dir}/{table}.uasset", "rb")
uexp = open(f"{base_dir}/{table}.uexp", "rb")
package = wacky.jsonify(uasset, uexp)
package_json = json.dumps(package, indent=4, sort_keys=True)
data=json.loads(package_json)
data = json.loads(package_json)
first_elem = data[0]
wacca_data = first_elem['rows']
wacca_data = first_elem["rows"]
for i, key in enumerate(wacca_data):
song_id = int(key)
title = wacca_data[str(key)]["MusicMessage"]
title = wacca_data[str(key)]["MusicMessage"]
artist = wacca_data[str(key)]["ArtistMessage"]
bpm = wacca_data[str(key)]["Bpm"]
jacket_asset_name = wacca_data[str(key)]["JacketAssetName"]
@@ -52,29 +67,69 @@ class WaccaReader(BaseReader):
designer = wacca_data[str(key)]["NotesDesignerNormal"]
if diff > 0:
self.data.static.put_music(self.version, song_id, 1, title, artist, bpm, diff, designer, jacket_asset_name)
self.data.static.put_music(
self.version,
song_id,
1,
title,
artist,
bpm,
diff,
designer,
jacket_asset_name,
)
self.logger.info(f"Read song {song_id} chart 1")
diff = float(wacca_data[str(key)]["DifficultyHardLv"])
designer = wacca_data[str(key)]["NotesDesignerHard"]
if diff > 0:
self.data.static.put_music(self.version, song_id, 2, title, artist, bpm, diff, designer, jacket_asset_name)
self.data.static.put_music(
self.version,
song_id,
2,
title,
artist,
bpm,
diff,
designer,
jacket_asset_name,
)
self.logger.info(f"Read song {song_id} chart 2")
diff = float(wacca_data[str(key)]["DifficultyExtremeLv"])
designer = wacca_data[str(key)]["NotesDesignerExpert"]
if diff > 0:
self.data.static.put_music(self.version, song_id, 3, title, artist, bpm, diff, designer, jacket_asset_name)
self.data.static.put_music(
self.version,
song_id,
3,
title,
artist,
bpm,
diff,
designer,
jacket_asset_name,
)
self.logger.info(f"Read song {song_id} chart 3")
diff = float(wacca_data[str(key)]["DifficultyInfernoLv"])
designer = wacca_data[str(key)]["NotesDesignerInferno"]
if diff > 0:
self.data.static.put_music(self.version, song_id, 4, title, artist, bpm, diff, designer, jacket_asset_name)
self.data.static.put_music(
self.version,
song_id,
4,
title,
artist,
bpm,
diff,
designer,
jacket_asset_name,
)
self.logger.info(f"Read song {song_id} chart 4")
def check_valid_pair(self, dir: str, file: str) -> bool:
return path.exists(f"{dir}/{file}.uasset") and path.exists(f"{dir}/{file}.uexp")
return path.exists(f"{dir}/{file}.uasset") and path.exists(f"{dir}/{file}.uexp")

View File

@@ -9,6 +9,7 @@ from titles.wacca.const import WaccaConstants
from titles.wacca.handlers import *
class WaccaReverse(WaccaLilyR):
def __init__(self, cfg: CoreConfig, game_cfg: WaccaConfig) -> None:
super().__init__(cfg, game_cfg)
@@ -46,12 +47,12 @@ class WaccaReverse(WaccaLilyR):
(310006, 0),
]
def handle_user_status_login_request(self, data: Dict)-> Dict:
def handle_user_status_login_request(self, data: Dict) -> Dict:
resp = super().handle_user_status_login_request(data)
resp["params"].append([])
return resp
def handle_user_status_getDetail_request(self, data: Dict)-> Dict:
def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
req = UserStatusGetDetailRequest(data)
resp = UserStatusGetDetailResponseV4()
@@ -79,15 +80,23 @@ class WaccaReverse(WaccaLilyR):
if profile["vip_expire_time"] is None:
resp.userStatus.vipExpireTime = 0
else:
resp.userStatus.vipExpireTime = int(profile["vip_expire_time"].timestamp())
resp.userStatus.vipExpireTime = int(profile["vip_expire_time"].timestamp())
if profile["always_vip"] or self.game_config.mods.always_vip:
resp.userStatus.vipExpireTime = int((self.srvtime + timedelta(days=31)).timestamp())
resp.userStatus.vipExpireTime = int(
(self.srvtime + timedelta(days=31)).timestamp()
)
resp.songUpdateTime = int(profile["last_login_date"].timestamp())
resp.lastSongInfo = LastSongDetail(profile["last_song_id"],profile["last_song_difficulty"],profile["last_folder_order"],profile["last_folder_id"],profile["last_song_order"])
resp.lastSongInfo = LastSongDetail(
profile["last_song_id"],
profile["last_song_difficulty"],
profile["last_folder_order"],
profile["last_folder_id"],
profile["last_song_order"],
)
resp.songPlayStatus = [resp.lastSongInfo.lastSongId, 1]
resp.userStatus.userId = profile["id"]
@@ -100,42 +109,57 @@ class WaccaReverse(WaccaLilyR):
resp.userStatus.loginDays = profile["login_count_days"]
resp.userStatus.loginConsecutiveDays = profile["login_count_days_consec"]
resp.userStatus.loginsToday = profile["login_count_today"]
resp.userStatus.rating = profile['rating']
resp.userStatus.rating = profile["rating"]
if self.game_config.mods.infinite_wp:
resp.userStatus.wp = 999999
for fav in profile_favorites:
resp.favorites.append(fav["song_id"])
if profile["friend_view_1"] is not None:
pass
if profile["friend_view_2"] is not None:
pass
if profile["friend_view_3"] is not None:
pass
resp.seasonalPlayModeCounts.append(PlayModeCounts(self.season, 1, profile["playcount_single"]))
resp.seasonalPlayModeCounts.append(PlayModeCounts(self.season, 2, profile["playcount_multi_vs"]))
resp.seasonalPlayModeCounts.append(PlayModeCounts(self.season, 3, profile["playcount_multi_coop"]))
resp.seasonalPlayModeCounts.append(PlayModeCounts(self.season, 4, profile["playcount_stageup"]))
resp.seasonalPlayModeCounts.append(
PlayModeCounts(self.season, 1, profile["playcount_single"])
)
resp.seasonalPlayModeCounts.append(
PlayModeCounts(self.season, 2, profile["playcount_multi_vs"])
)
resp.seasonalPlayModeCounts.append(
PlayModeCounts(self.season, 3, profile["playcount_multi_coop"])
)
resp.seasonalPlayModeCounts.append(
PlayModeCounts(self.season, 4, profile["playcount_stageup"])
)
for opt in profile_options:
resp.options.append(UserOption(opt["opt_id"], opt["value"]))
if profile_bingo is not None:
resp.bingoStatus = BingoDetail(profile_bingo["page_number"])
for x in profile_bingo["page_progress"]:
resp.bingoStatus.pageStatus.append(BingoPageStatus(x[0], x[1], x[2]))
for gate in self.game_config.gates.enabled_gates:
added_gate = False
for user_gate in profile_gates:
if user_gate["gate_id"] == gate:
resp.gateInfo.append(GateDetailV2(user_gate["gate_id"],user_gate["page"],user_gate["progress"],
user_gate["loops"],int(user_gate["last_used"].timestamp()),user_gate["mission_flag"]))
resp.gateInfo.append(
GateDetailV2(
user_gate["gate_id"],
user_gate["page"],
user_gate["progress"],
user_gate["loops"],
int(user_gate["last_used"].timestamp()),
user_gate["mission_flag"],
)
)
resp.seasonInfo.cumulativeGatePts += user_gate["total_points"]
@@ -144,14 +168,18 @@ class WaccaReverse(WaccaLilyR):
if not added_gate:
resp.gateInfo.append(GateDetailV2(gate))
for unlock in profile_song_unlocks:
for x in range(1, unlock["highest_difficulty"] + 1):
resp.userItems.songUnlocks.append(SongUnlock(unlock["song_id"], x, 0, int(unlock["acquire_date"].timestamp())))
resp.userItems.songUnlocks.append(
SongUnlock(
unlock["song_id"], x, 0, int(unlock["acquire_date"].timestamp())
)
)
for song in profile_scores:
resp.seasonInfo.cumulativeScore += song["score"]
clear_cts = SongDetailClearCounts(
song["play_ct"],
song["clear_ct"],
@@ -161,13 +189,23 @@ class WaccaReverse(WaccaLilyR):
)
grade_cts = SongDetailGradeCountsV2(
song["grade_d_ct"], song["grade_c_ct"], song["grade_b_ct"], song["grade_a_ct"], song["grade_aa_ct"],
song["grade_aaa_ct"], song["grade_s_ct"], song["grade_ss_ct"], song["grade_sss_ct"],
song["grade_master_ct"], song["grade_sp_ct"], song["grade_ssp_ct"], song["grade_sssp_ct"]
song["grade_d_ct"],
song["grade_c_ct"],
song["grade_b_ct"],
song["grade_a_ct"],
song["grade_aa_ct"],
song["grade_aaa_ct"],
song["grade_s_ct"],
song["grade_ss_ct"],
song["grade_sss_ct"],
song["grade_master_ct"],
song["grade_sp_ct"],
song["grade_ssp_ct"],
song["grade_sssp_ct"],
)
deets = BestScoreDetailV2(song["song_id"], song["chart_id"])
deets.clearCounts = clear_cts
deets.clearCounts = clear_cts
deets.clearCountsSeason = clear_cts
deets.gradeCounts = grade_cts
deets.score = song["score"]
@@ -176,9 +214,16 @@ class WaccaReverse(WaccaLilyR):
deets.rating = song["rating"]
resp.scores.append(deets)
for trophy in profile_trophies:
resp.userItems.trophies.append(TrophyItem(trophy["trophy_id"], trophy["season"], trophy["progress"], trophy["badge_type"]))
resp.userItems.trophies.append(
TrophyItem(
trophy["trophy_id"],
trophy["season"],
trophy["progress"],
trophy["badge_type"],
)
)
if self.game_config.mods.infinite_tickets:
for x in range(5):
@@ -190,30 +235,48 @@ class WaccaReverse(WaccaLilyR):
else:
expire = int(ticket["expire_date"].timestamp())
resp.userItems.tickets.append(TicketItem(ticket["id"], ticket["ticket_id"], expire))
resp.userItems.tickets.append(
TicketItem(ticket["id"], ticket["ticket_id"], expire)
)
if profile_items:
for item in profile_items:
try:
if item["type"] == WaccaConstants.ITEM_TYPES["icon"]:
resp.userItems.icons.append(IconItem(item["item_id"], 1, item["use_count"], int(item["acquire_date"].timestamp())))
resp.userItems.icons.append(
IconItem(
item["item_id"],
1,
item["use_count"],
int(item["acquire_date"].timestamp()),
)
)
elif item["type"] == WaccaConstants.ITEM_TYPES["navigator"]:
resp.userItems.navigators.append(NavigatorItem(item["item_id"], 1, int(item["acquire_date"].timestamp()), item["use_count"], item["use_count"]))
resp.userItems.navigators.append(
NavigatorItem(
item["item_id"],
1,
int(item["acquire_date"].timestamp()),
item["use_count"],
item["use_count"],
)
)
else:
itm_send = GenericItemSend(item["item_id"], 1, int(item["acquire_date"].timestamp()))
itm_send = GenericItemSend(
item["item_id"], 1, int(item["acquire_date"].timestamp())
)
if item["type"] == WaccaConstants.ITEM_TYPES["title"]:
resp.userItems.titles.append(itm_send)
elif item["type"] == WaccaConstants.ITEM_TYPES["user_plate"]:
resp.userItems.plates.append(itm_send)
elif item["type"] == WaccaConstants.ITEM_TYPES["touch_effect"]:
resp.userItems.touchEffect.append(itm_send)
elif item["type"] == WaccaConstants.ITEM_TYPES["note_color"]:
resp.userItems.noteColors.append(itm_send)
@@ -221,7 +284,9 @@ class WaccaReverse(WaccaLilyR):
resp.userItems.noteSounds.append(itm_send)
except:
self.logger.error(f"{__name__} Failed to load item {item['item_id']} for user {profile['user']}")
self.logger.error(
f"{__name__} Failed to load item {item['item_id']} for user {profile['user']}"
)
resp.seasonInfo.level = profile["xp"]
resp.seasonInfo.wpObtained = profile["wp_total"]
@@ -234,12 +299,15 @@ class WaccaReverse(WaccaLilyR):
return resp.make()
def handle_user_status_create_request(self, data: Dict)-> Dict:
def handle_user_status_create_request(self, data: Dict) -> Dict:
req = UserStatusCreateRequest(data)
resp = super().handle_user_status_create_request(data)
self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310001) # Added reverse
self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310002) # Added reverse
return resp
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310001
) # Added reverse
self.data.item.put_item(
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310002
) # Added reverse
return resp

View File

@@ -9,6 +9,7 @@ from titles.wacca.const import WaccaConstants
from titles.wacca.handlers import *
class WaccaS(WaccaBase):
allowed_stages = [
(1513, 13),
@@ -25,11 +26,11 @@ class WaccaS(WaccaBase):
(1512, 2),
(1511, 1),
]
def __init__(self, cfg: CoreConfig, game_cfg: WaccaConfig) -> None:
super().__init__(cfg, game_cfg)
self.version = WaccaConstants.VER_WACCA_S
def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
resp = GetNewsResponseV2()
return resp.make()

View File

@@ -3,4 +3,4 @@ from titles.wacca.schema.score import WaccaScoreData
from titles.wacca.schema.item import WaccaItemData
from titles.wacca.schema.static import WaccaStaticData
__all__ = ["WaccaProfileData", "WaccaScoreData", "WaccaItemData", "WaccaStaticData"]
__all__ = ["WaccaProfileData", "WaccaScoreData", "WaccaItemData", "WaccaStaticData"]

View File

@@ -12,132 +12,158 @@ item = Table(
"wacca_item",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("item_id", Integer, nullable=False),
Column("type", Integer, nullable=False),
Column("type", Integer, nullable=False),
Column("acquire_date", TIMESTAMP, nullable=False, server_default=func.now()),
Column("use_count", Integer, server_default="0"),
UniqueConstraint("user", "item_id", "type", name="wacca_item_uk"),
mysql_charset='utf8mb4'
mysql_charset="utf8mb4",
)
ticket = Table(
"wacca_ticket",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("ticket_id", Integer, nullable=False),
Column("acquire_date", TIMESTAMP, nullable=False, server_default=func.now()),
Column("expire_date", TIMESTAMP),
mysql_charset='utf8mb4'
mysql_charset="utf8mb4",
)
song_unlock = Table(
"wacca_song_unlock",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("song_id", Integer, nullable=False),
Column("highest_difficulty", Integer, nullable=False),
Column("highest_difficulty", Integer, nullable=False),
Column("acquire_date", TIMESTAMP, nullable=False, server_default=func.now()),
UniqueConstraint("user", "song_id", name="wacca_song_unlock_uk"),
mysql_charset='utf8mb4'
mysql_charset="utf8mb4",
)
trophy = Table(
"wacca_trophy",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("trophy_id", Integer, nullable=False),
Column("season", Integer, nullable=False),
Column("progress", Integer, nullable=False, server_default="0"),
Column("badge_type", Integer, nullable=False, server_default="0"),
UniqueConstraint("user", "trophy_id", "season", name="wacca_trophy_uk"),
mysql_charset='utf8mb4'
mysql_charset="utf8mb4",
)
class WaccaItemData(BaseData):
class WaccaItemData(BaseData):
def get_song_unlocks(self, user_id: int) -> Optional[List[Row]]:
sql = song_unlock.select(song_unlock.c.user == user_id)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchall()
def unlock_song(self, user_id: int, song_id: int, difficulty: int) -> Optional[int]:
sql = insert(song_unlock).values(
user=user_id,
song_id=song_id,
highest_difficulty=difficulty
user=user_id, song_id=song_id, highest_difficulty=difficulty
)
conflict = sql.on_duplicate_key_update(
highest_difficulty=case(
(song_unlock.c.highest_difficulty >= difficulty, song_unlock.c.highest_difficulty),
(
song_unlock.c.highest_difficulty >= difficulty,
song_unlock.c.highest_difficulty,
),
(song_unlock.c.highest_difficulty < difficulty, difficulty),
)
)
result = self.execute(conflict)
if result is None:
self.logger.error(f"{__name__} failed to unlock song! user: {user_id}, song_id: {song_id}, difficulty: {difficulty}")
self.logger.error(
f"{__name__} failed to unlock song! user: {user_id}, song_id: {song_id}, difficulty: {difficulty}"
)
return None
return result.lastrowid
def put_item(self, user_id: int, item_type: int, item_id: int) -> Optional[int]:
sql = insert(item).values(
user = user_id,
item_id = item_id,
type = item_type,
user=user_id,
item_id=item_id,
type=item_type,
)
conflict = sql.on_duplicate_key_update(
use_count = item.c.use_count + 1
)
conflict = sql.on_duplicate_key_update(use_count=item.c.use_count + 1)
result = self.execute(conflict)
if result is None:
self.logger.error(f"{__name__} failed to insert item! user: {user_id}, item_id: {item_id}, item_type: {item_type}")
self.logger.error(
f"{__name__} failed to insert item! user: {user_id}, item_id: {item_id}, item_type: {item_type}"
)
return None
return result.lastrowid
def get_items(self, user_id: int, item_type: int = None, item_id: int = None) -> Optional[List[Row]]:
def get_items(
self, user_id: int, item_type: int = None, item_id: int = None
) -> Optional[List[Row]]:
"""
A catch-all item lookup given a profile and option item type and ID specifiers
"""
sql = item.select(
and_(item.c.user == user_id,
item.c.type == item_type if item_type is not None else True,
item.c.item_id == item_id if item_id is not None else True)
)
result = self.execute(sql)
if result is None: return None
return result.fetchall()
def get_tickets(self, user_id: int) -> Optional[List[Row]]:
sql = select(ticket).where(ticket.c.user == user_id)
result = self.execute(sql)
if result is None: return None
return result.fetchall()
def add_ticket(self, user_id: int, ticket_id: int) -> None:
sql = insert(ticket).values(
user = user_id,
ticket_id = ticket_id
and_(
item.c.user == user_id,
item.c.type == item_type if item_type is not None else True,
item.c.item_id == item_id if item_id is not None else True,
)
)
result = self.execute(sql)
if result is None:
self.logger.error(f"add_ticket: Failed to insert wacca ticket! user_id: {user_id} ticket_id {ticket_id}")
return None
return result.fetchall()
def get_tickets(self, user_id: int) -> Optional[List[Row]]:
sql = select(ticket).where(ticket.c.user == user_id)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def add_ticket(self, user_id: int, ticket_id: int) -> None:
sql = insert(ticket).values(user=user_id, ticket_id=ticket_id)
result = self.execute(sql)
if result is None:
self.logger.error(
f"add_ticket: Failed to insert wacca ticket! user_id: {user_id} ticket_id {ticket_id}"
)
return None
return result.lastrowid
def spend_ticket(self, id: int) -> None:
sql = delete(ticket).where(ticket.c.id == id)
@@ -146,32 +172,36 @@ class WaccaItemData(BaseData):
self.logger.warn(f"Failed to delete ticket id {id}")
return None
def get_trophies(self, user_id: int, season: int = None) -> Optional[List[Row]]:
def get_trophies(self, user_id: int, season: int = None) -> Optional[List[Row]]:
if season is None:
sql = select(trophy).where(trophy.c.user == user_id)
else:
sql = select(trophy).where(and_(trophy.c.user == user_id, trophy.c.season == season))
sql = select(trophy).where(
and_(trophy.c.user == user_id, trophy.c.season == season)
)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchall()
def update_trophy(self, user_id: int, trophy_id: int, season: int, progress: int, badge_type: int) -> Optional[int]:
def update_trophy(
self, user_id: int, trophy_id: int, season: int, progress: int, badge_type: int
) -> Optional[int]:
sql = insert(trophy).values(
user = user_id,
trophy_id = trophy_id,
season = season,
progress = progress,
badge_type = badge_type
user=user_id,
trophy_id=trophy_id,
season=season,
progress=progress,
badge_type=badge_type,
)
conflict = sql.on_duplicate_key_update(
progress = progress
)
conflict = sql.on_duplicate_key_update(progress=progress)
result = self.execute(conflict)
if result is None:
self.logger.error(f"update_trophy: Failed to insert wacca trophy! user_id: {user_id} trophy_id: {trophy_id} progress {progress}")
self.logger.error(
f"update_trophy: Failed to insert wacca trophy! user_id: {user_id} trophy_id: {trophy_id} progress {progress}"
)
return None
return result.lastrowid

View File

@@ -12,7 +12,11 @@ profile = Table(
"wacca_profile",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("version", Integer),
Column("username", String(8), nullable=False),
Column("xp", Integer, server_default="0"),
@@ -20,7 +24,7 @@ profile = Table(
Column("wp_total", Integer, server_default="0"),
Column("wp_spent", Integer, server_default="0"),
Column("dan_type", Integer, server_default="0"),
Column("dan_level", Integer, server_default="0"),
Column("dan_level", Integer, server_default="0"),
Column("title_0", Integer, server_default="0"),
Column("title_1", Integer, server_default="0"),
Column("title_2", Integer, server_default="0"),
@@ -48,14 +52,18 @@ profile = Table(
Column("last_login_date", TIMESTAMP, server_default=func.now()),
Column("gate_tutorial_flags", JSON),
UniqueConstraint("user", "version", name="wacca_profile_uk"),
mysql_charset='utf8mb4'
mysql_charset="utf8mb4",
)
option = Table(
"wacca_option",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("opt_id", Integer, nullable=False),
Column("value", Integer, nullable=False),
UniqueConstraint("user", "opt_id", name="wacca_option_uk"),
@@ -64,38 +72,59 @@ option = Table(
bingo = Table(
"wacca_bingo",
metadata,
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
primary_key=True,
nullable=False,
),
Column("page_number", Integer, nullable=False),
Column("page_progress", JSON, nullable=False),
UniqueConstraint("user", "page_number", name="wacca_bingo_uk"),
mysql_charset='utf8mb4'
mysql_charset="utf8mb4",
)
friend = Table(
"wacca_friend",
metadata,
Column("profile_sender", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column("profile_reciever", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"profile_sender",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column(
"profile_reciever",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("is_accepted", Boolean, server_default="0"),
PrimaryKeyConstraint('profile_sender', 'profile_reciever', name='arcade_owner_pk'),
mysql_charset='utf8mb4'
PrimaryKeyConstraint("profile_sender", "profile_reciever", name="arcade_owner_pk"),
mysql_charset="utf8mb4",
)
favorite = Table(
"wacca_favorite_song",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("song_id", Integer, nullable=False),
UniqueConstraint("user", "song_id", name="wacca_favorite_song_uk"),
mysql_charset='utf8mb4'
mysql_charset="utf8mb4",
)
gate = Table(
"wacca_gate",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("gate_id", Integer, nullable=False),
Column("page", Integer, nullable=False, server_default="0"),
Column("progress", Integer, nullable=False, server_default="0"),
@@ -106,68 +135,87 @@ gate = Table(
UniqueConstraint("user", "gate_id", name="wacca_gate_uk"),
)
class WaccaProfileData(BaseData):
def create_profile(self, aime_id: int, username: str, version: int) -> Optional[int]:
def create_profile(
self, aime_id: int, username: str, version: int
) -> Optional[int]:
"""
Given a game version, aime id, and username, create a profile and return it's ID
"""
sql = insert(profile).values(
user=aime_id,
username=username,
version=version
)
sql = insert(profile).values(user=aime_id, username=username, version=version)
conflict = sql.on_duplicate_key_update(
username = sql.inserted.username
)
conflict = sql.on_duplicate_key_update(username=sql.inserted.username)
result = self.execute(conflict)
if result is None:
self.logger.error(f"{__name__} Failed to insert wacca profile! aime id: {aime_id} username: {username}")
self.logger.error(
f"{__name__} Failed to insert wacca profile! aime id: {aime_id} username: {username}"
)
return None
return result.lastrowid
def update_profile_playtype(self, profile_id: int, play_type: int, game_version: str) -> None:
def update_profile_playtype(
self, profile_id: int, play_type: int, game_version: str
) -> None:
sql = profile.update(profile.c.id == profile_id).values(
playcount_single = profile.c.playcount_single + 1 if play_type == 1 else profile.c.playcount_single,
playcount_multi_vs = profile.c.playcount_multi_vs + 1 if play_type == 2 else profile.c.playcount_multi_vs,
playcount_multi_coop = profile.c.playcount_multi_coop + 1 if play_type == 3 else profile.c.playcount_multi_coop,
playcount_stageup = profile.c.playcount_stageup + 1 if play_type == 4 else profile.c.playcount_stageup,
last_game_ver = game_version,
)
result = self.execute(sql)
if result is None:
self.logger.error(f"update_profile: failed to update profile! profile: {profile_id}")
return None
def update_profile_lastplayed(self, profile_id: int, last_song_id: int, last_song_difficulty: int, last_folder_order: int,
last_folder_id: int, last_song_order: int) -> None:
sql = profile.update(profile.c.id == profile_id).values(
last_song_id = last_song_id,
last_song_difficulty = last_song_difficulty,
last_folder_order = last_folder_order,
last_folder_id = last_folder_id,
last_song_order = last_song_order
)
result = self.execute(sql)
if result is None:
self.logger.error(f"update_profile_lastplayed: failed to update profile! profile: {profile_id}")
return None
def update_profile_dan(self, profile_id: int, dan_level: int, dan_type: int) -> Optional[int]:
sql = profile.update(profile.c.id == profile_id).values(
dan_level = dan_level,
dan_type = dan_type
playcount_single=profile.c.playcount_single + 1
if play_type == 1
else profile.c.playcount_single,
playcount_multi_vs=profile.c.playcount_multi_vs + 1
if play_type == 2
else profile.c.playcount_multi_vs,
playcount_multi_coop=profile.c.playcount_multi_coop + 1
if play_type == 3
else profile.c.playcount_multi_coop,
playcount_stageup=profile.c.playcount_stageup + 1
if play_type == 4
else profile.c.playcount_stageup,
last_game_ver=game_version,
)
result = self.execute(sql)
if result is None:
self.logger.warn(f"update_profile_dan: Failed to update! profile {profile_id}")
self.logger.error(
f"update_profile: failed to update profile! profile: {profile_id}"
)
return None
def update_profile_lastplayed(
self,
profile_id: int,
last_song_id: int,
last_song_difficulty: int,
last_folder_order: int,
last_folder_id: int,
last_song_order: int,
) -> None:
sql = profile.update(profile.c.id == profile_id).values(
last_song_id=last_song_id,
last_song_difficulty=last_song_difficulty,
last_folder_order=last_folder_order,
last_folder_id=last_folder_id,
last_song_order=last_song_order,
)
result = self.execute(sql)
if result is None:
self.logger.error(
f"update_profile_lastplayed: failed to update profile! profile: {profile_id}"
)
return None
def update_profile_dan(
self, profile_id: int, dan_level: int, dan_type: int
) -> Optional[int]:
sql = profile.update(profile.c.id == profile_id).values(
dan_level=dan_level, dan_type=dan_type
)
result = self.execute(sql)
if result is None:
self.logger.warn(
f"update_profile_dan: Failed to update! profile {profile_id}"
)
return None
return result.lastrowid
@@ -180,11 +228,14 @@ class WaccaProfileData(BaseData):
elif profile_id > 0:
sql = profile.select(profile.c.id == profile_id)
else:
self.logger.error(f"get_profile: Bad arguments!! profile_id {profile_id} aime_id {aime_id}")
self.logger.error(
f"get_profile: Bad arguments!! profile_id {profile_id} aime_id {aime_id}"
)
return None
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchone()
def get_options(self, user_id: int, option_id: int = None) -> Optional[List[Row]]:
@@ -192,71 +243,83 @@ class WaccaProfileData(BaseData):
Get a specific user option for a profile, or all of them if none specified
"""
sql = option.select(
and_(option.c.user == user_id,
option.c.opt_id == option_id if option_id is not None else True)
and_(
option.c.user == user_id,
option.c.opt_id == option_id if option_id is not None else True,
)
)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
if option_id is not None:
return result.fetchone()
else:
return result.fetchall()
def update_option(self, user_id: int, option_id: int, value: int) -> Optional[int]:
sql = insert(option).values(
user = user_id,
opt_id = option_id,
value = value
)
conflict = sql.on_duplicate_key_update(
value = value
)
def update_option(self, user_id: int, option_id: int, value: int) -> Optional[int]:
sql = insert(option).values(user=user_id, opt_id=option_id, value=value)
conflict = sql.on_duplicate_key_update(value=value)
result = self.execute(conflict)
if result is None:
self.logger.error(f"{__name__} failed to insert option! profile: {user_id}, option: {option_id}, value: {value}")
self.logger.error(
f"{__name__} failed to insert option! profile: {user_id}, option: {option_id}, value: {value}"
)
return None
return result.lastrowid
def add_favorite_song(self, user_id: int, song_id: int) -> Optional[int]:
sql = favorite.insert().values(
user=user_id,
song_id=song_id
)
sql = favorite.insert().values(user=user_id, song_id=song_id)
result = self.execute(sql)
if result is None:
self.logger.error(f"{__name__} failed to insert favorite! profile: {user_id}, song_id: {song_id}")
if result is None:
self.logger.error(
f"{__name__} failed to insert favorite! profile: {user_id}, song_id: {song_id}"
)
return None
return result.lastrowid
def remove_favorite_song(self, user_id: int, song_id: int) -> None:
sql = favorite.delete(and_(favorite.c.user == user_id, favorite.c.song_id == song_id))
sql = favorite.delete(
and_(favorite.c.user == user_id, favorite.c.song_id == song_id)
)
result = self.execute(sql)
if result is None:
self.logger.error(f"{__name__} failed to remove favorite! profile: {user_id}, song_id: {song_id}")
if result is None:
self.logger.error(
f"{__name__} failed to remove favorite! profile: {user_id}, song_id: {song_id}"
)
return None
def get_favorite_songs(self, user_id: int) -> Optional[List[Row]]:
sql = favorite.select(favorite.c.user == user_id)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchall()
def get_gates(self, user_id: int) -> Optional[List[Row]]:
sql = select(gate).where(gate.c.user == user_id)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchall()
def update_gate(self, user_id: int, gate_id: int, page: int, progress: int, loop: int, mission_flag: int,
total_points: int) -> Optional[int]:
def update_gate(
self,
user_id: int,
gate_id: int,
page: int,
progress: int,
loop: int,
mission_flag: int,
total_points: int,
) -> Optional[int]:
sql = insert(gate).values(
user=user_id,
gate_id=gate_id,
@@ -264,7 +327,7 @@ class WaccaProfileData(BaseData):
progress=progress,
loops=loop,
mission_flag=mission_flag,
total_points=total_points
total_points=total_points,
)
conflict = sql.on_duplicate_key_update(
@@ -276,16 +339,19 @@ class WaccaProfileData(BaseData):
)
result = self.execute(conflict)
if result is None:
self.logger.error(f"{__name__} failed to update gate! user: {user_id}, gate_id: {gate_id}")
if result is None:
self.logger.error(
f"{__name__} failed to update gate! user: {user_id}, gate_id: {gate_id}"
)
return None
return result.lastrowid
def get_friends(self, user_id: int) -> Optional[List[Row]]:
sql = friend.select(friend.c.profile_sender == user_id)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchall()
def profile_to_aime_user(self, profile_id: int) -> Optional[int]:
@@ -293,136 +359,159 @@ class WaccaProfileData(BaseData):
result = self.execute(sql)
if result is None:
self.logger.info(f"profile_to_aime_user: No user found for profile {profile_id}")
self.logger.info(
f"profile_to_aime_user: No user found for profile {profile_id}"
)
return None
this_profile = result.fetchone()
if this_profile is None:
self.logger.info(f"profile_to_aime_user: No user found for profile {profile_id}")
self.logger.info(
f"profile_to_aime_user: No user found for profile {profile_id}"
)
return None
return this_profile['user']
def session_login(self, profile_id: int, is_new_day: bool, is_consec_day: bool) -> None:
return this_profile["user"]
def session_login(
self, profile_id: int, is_new_day: bool, is_consec_day: bool
) -> None:
# TODO: Reset consec days counter
sql = profile.update(profile.c.id == profile_id).values(
login_count = profile.c.login_count + 1,
login_count_consec = profile.c.login_count_consec + 1,
login_count_days = profile.c.login_count_days + 1 if is_new_day else profile.c.login_count_days,
login_count_days_consec = profile.c.login_count_days_consec + 1 if is_new_day and is_consec_day else profile.c.login_count_days_consec,
login_count_today = 1 if is_new_day else profile.c.login_count_today + 1,
last_login_date = func.now()
)
result = self.execute(sql)
if result is None:
self.logger.error(f"session_login: failed to update profile! profile: {profile_id}")
return None
def session_logout(self, profile_id: int) -> None:
sql = profile.update(profile.c.id == id).values(
login_count_consec = 0
)
result = self.execute(sql)
if result is None:
self.logger.error(f"{__name__} failed to update profile! profile: {profile_id}")
return None
def add_xp(self, profile_id: int, xp: int) -> None:
sql = profile.update(profile.c.id == profile_id).values(
xp = profile.c.xp + xp
login_count=profile.c.login_count + 1,
login_count_consec=profile.c.login_count_consec + 1,
login_count_days=profile.c.login_count_days + 1
if is_new_day
else profile.c.login_count_days,
login_count_days_consec=profile.c.login_count_days_consec + 1
if is_new_day and is_consec_day
else profile.c.login_count_days_consec,
login_count_today=1 if is_new_day else profile.c.login_count_today + 1,
last_login_date=func.now(),
)
result = self.execute(sql)
if result is None:
self.logger.error(f"add_xp: Failed to update profile! profile_id {profile_id} xp {xp}")
self.logger.error(
f"session_login: failed to update profile! profile: {profile_id}"
)
return None
def session_logout(self, profile_id: int) -> None:
sql = profile.update(profile.c.id == id).values(login_count_consec=0)
result = self.execute(sql)
if result is None:
self.logger.error(
f"{__name__} failed to update profile! profile: {profile_id}"
)
return None
def add_xp(self, profile_id: int, xp: int) -> None:
sql = profile.update(profile.c.id == profile_id).values(xp=profile.c.xp + xp)
result = self.execute(sql)
if result is None:
self.logger.error(
f"add_xp: Failed to update profile! profile_id {profile_id} xp {xp}"
)
return None
def add_wp(self, profile_id: int, wp: int) -> None:
sql = profile.update(profile.c.id == profile_id).values(
wp = profile.c.wp + wp,
wp_total = profile.c.wp_total + wp,
wp=profile.c.wp + wp,
wp_total=profile.c.wp_total + wp,
)
result = self.execute(sql)
if result is None:
self.logger.error(f"add_wp: Failed to update profile! profile_id {profile_id} wp {wp}")
self.logger.error(
f"add_wp: Failed to update profile! profile_id {profile_id} wp {wp}"
)
return None
def spend_wp(self, profile_id: int, wp: int) -> None:
sql = profile.update(profile.c.id == profile_id).values(
wp = profile.c.wp - wp,
wp_spent = profile.c.wp_spent + wp,
wp=profile.c.wp - wp,
wp_spent=profile.c.wp_spent + wp,
)
result = self.execute(sql)
if result is None:
self.logger.error(f"spend_wp: Failed to update profile! profile_id {profile_id} wp {wp}")
self.logger.error(
f"spend_wp: Failed to update profile! profile_id {profile_id} wp {wp}"
)
return None
def activate_vip(self, profile_id: int, expire_time) -> None:
sql = profile.update(profile.c.id == profile_id).values(
vip_expire_time = expire_time
vip_expire_time=expire_time
)
result = self.execute(sql)
if result is None:
self.logger.error(f"activate_vip: Failed to update profile! profile_id {profile_id} expire_time {expire_time}")
self.logger.error(
f"activate_vip: Failed to update profile! profile_id {profile_id} expire_time {expire_time}"
)
return None
def update_user_rating(self, profile_id: int, new_rating: int) -> None:
sql = profile.update(profile.c.id == profile_id).values(
rating = new_rating
)
sql = profile.update(profile.c.id == profile_id).values(rating=new_rating)
result = self.execute(sql)
if result is None:
self.logger.error(f"update_user_rating: Failed to update profile! profile_id {profile_id} new_rating {new_rating}")
self.logger.error(
f"update_user_rating: Failed to update profile! profile_id {profile_id} new_rating {new_rating}"
)
return None
def update_bingo(self, aime_id: int, page: int, progress: int) -> Optional[int]:
sql = insert(bingo).values(
user=aime_id,
page_number=page,
page_progress=progress
)
conflict = sql.on_duplicate_key_update(
page_number=page,
page_progress=progress
user=aime_id, page_number=page, page_progress=progress
)
conflict = sql.on_duplicate_key_update(page_number=page, page_progress=progress)
result = self.execute(conflict)
if result is None:
if result is None:
self.logger.error(f"put_bingo: failed to update! aime_id: {aime_id}")
return None
return result.lastrowid
def get_bingo(self, aime_id: int) -> Optional[List[Row]]:
sql = select(bingo).where(bingo.c.user==aime_id)
sql = select(bingo).where(bingo.c.user == aime_id)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchone()
def get_bingo_page(self, aime_id: int, page: Dict) -> Optional[List[Row]]:
sql = select(bingo).where(and_(bingo.c.user==aime_id, bingo.c.page_number==page))
sql = select(bingo).where(
and_(bingo.c.user == aime_id, bingo.c.page_number == page)
)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchone()
def update_vip_time(self, profile_id: int, time_left) -> None:
sql = profile.update(profile.c.id == profile_id).values(vip_expire_time = time_left)
sql = profile.update(profile.c.id == profile_id).values(
vip_expire_time=time_left
)
result = self.execute(sql)
if result is None:
self.logger.error(f"Failed to update VIP time for profile {profile_id}")
def update_tutorial_flags(self, profile_id: int, flags: Dict) -> None:
sql = profile.update(profile.c.id == profile_id).values(gate_tutorial_flags = flags)
sql = profile.update(profile.c.id == profile_id).values(
gate_tutorial_flags=flags
)
result = self.execute(sql)
if result is None:
self.logger.error(f"Failed to update tutorial flags for profile {profile_id}")
self.logger.error(
f"Failed to update tutorial flags for profile {profile_id}"
)

View File

@@ -13,9 +13,13 @@ best_score = Table(
"wacca_score_best",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("song_id", Integer),
Column("chart_id", Integer),
Column("chart_id", Integer),
Column("score", Integer),
Column("play_ct", Integer),
Column("clear_ct", Integer),
@@ -39,14 +43,18 @@ best_score = Table(
Column("lowest_miss_ct", Integer),
Column("rating", Integer),
UniqueConstraint("user", "song_id", "chart_id", name="wacca_score_uk"),
mysql_charset='utf8mb4'
mysql_charset="utf8mb4",
)
playlog = Table(
"wacca_score_playlog",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("song_id", Integer),
Column("chart_id", Integer),
Column("score", Integer),
@@ -61,14 +69,18 @@ playlog = Table(
Column("late_ct", Integer),
Column("season", Integer),
Column("date_scored", TIMESTAMP, server_default=func.now()),
mysql_charset='utf8mb4'
mysql_charset="utf8mb4",
)
stageup = Table(
"wacca_score_stageup",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("version", Integer),
Column("stage_id", Integer),
Column("clear_status", Integer),
@@ -77,19 +89,29 @@ stageup = Table(
Column("song2_score", Integer),
Column("song3_score", Integer),
Column("play_ct", Integer, server_default="1"),
UniqueConstraint("user", "stage_id", name="wacca_score_stageup_uk"),
mysql_charset='utf8mb4'
UniqueConstraint("user", "stage_id", name="wacca_score_stageup_uk"),
mysql_charset="utf8mb4",
)
class WaccaScoreData(BaseData):
def put_best_score(self, user_id: int, song_id: int, chart_id: int, score: int, clear: List[int],
grade: List[int], best_combo: int, lowest_miss_ct: int) -> Optional[int]:
def put_best_score(
self,
user_id: int,
song_id: int,
chart_id: int,
score: int,
clear: List[int],
grade: List[int],
best_combo: int,
lowest_miss_ct: int,
) -> Optional[int]:
"""
Update the user's best score for a chart
"""
while len(grade) < 13:
grade.append(0)
sql = insert(best_score).values(
user=user_id,
song_id=song_id,
@@ -115,7 +137,7 @@ class WaccaScoreData(BaseData):
grade_sssp_ct=grade[12],
best_combo=best_combo,
lowest_miss_ct=lowest_miss_ct,
rating=0
rating=0,
)
conflict = sql.on_duplicate_key_update(
@@ -144,13 +166,30 @@ class WaccaScoreData(BaseData):
result = self.execute(conflict)
if result is None:
self.logger.error(f"{__name__}: failed to insert best score! profile: {user_id}, song: {song_id}, chart: {chart_id}")
self.logger.error(
f"{__name__}: failed to insert best score! profile: {user_id}, song: {song_id}, chart: {chart_id}"
)
return None
return result.lastrowid
def put_playlog(self, user_id: int, song_id: int, chart_id: int, this_score: int, clear: int, grade: int, max_combo: int,
marv_ct: int, great_ct: int, good_ct: int, miss_ct: int, fast_ct: int, late_ct: int, season: int) -> Optional[int]:
def put_playlog(
self,
user_id: int,
song_id: int,
chart_id: int,
this_score: int,
clear: int,
grade: int,
max_combo: int,
marv_ct: int,
great_ct: int,
good_ct: int,
miss_ct: int,
fast_ct: int,
late_ct: int,
season: int,
) -> Optional[int]:
"""
Add an entry to the user's play log
"""
@@ -168,85 +207,112 @@ class WaccaScoreData(BaseData):
miss_ct=miss_ct,
fast_ct=fast_ct,
late_ct=late_ct,
season=season
season=season,
)
result = self.execute(sql)
if result is None:
self.logger.error(f"{__name__} failed to insert playlog! profile: {user_id}, song: {song_id}, chart: {chart_id}")
self.logger.error(
f"{__name__} failed to insert playlog! profile: {user_id}, song: {song_id}, chart: {chart_id}"
)
return None
return result.lastrowid
def get_best_score(self, user_id: int, song_id: int, chart_id: int) -> Optional[Row]:
def get_best_score(
self, user_id: int, song_id: int, chart_id: int
) -> Optional[Row]:
sql = best_score.select(
and_(best_score.c.user == user_id, best_score.c.song_id == song_id, best_score.c.chart_id == chart_id)
and_(
best_score.c.user == user_id,
best_score.c.song_id == song_id,
best_score.c.chart_id == chart_id,
)
)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchone()
def get_best_scores(self, user_id: int) -> Optional[List[Row]]:
sql = best_score.select(
best_score.c.user == user_id
)
sql = best_score.select(best_score.c.user == user_id)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchall()
def update_song_rating(self, user_id: int, song_id: int, chart_id: int, new_rating: int) -> None:
def update_song_rating(
self, user_id: int, song_id: int, chart_id: int, new_rating: int
) -> None:
sql = best_score.update(
and_(
best_score.c.user == user_id,
best_score.c.song_id == song_id,
best_score.c.chart_id == chart_id
)).values(
rating = new_rating
best_score.c.user == user_id,
best_score.c.song_id == song_id,
best_score.c.chart_id == chart_id,
)
).values(rating=new_rating)
result = self.execute(sql)
if result is None:
self.logger.error(f"update_song_rating: failed to update rating! user_id: {user_id} song_id: {song_id} chart_id {chart_id} new_rating {new_rating}")
if result is None:
self.logger.error(
f"update_song_rating: failed to update rating! user_id: {user_id} song_id: {song_id} chart_id {chart_id} new_rating {new_rating}"
)
return None
def put_stageup(self, user_id: int, version: int, stage_id: int, clear_status: int, clear_song_ct: int, score1: int,
score2: int, score3: int) -> Optional[int]:
def put_stageup(
self,
user_id: int,
version: int,
stage_id: int,
clear_status: int,
clear_song_ct: int,
score1: int,
score2: int,
score3: int,
) -> Optional[int]:
sql = insert(stageup).values(
user = user_id,
version = version,
stage_id = stage_id,
clear_status = clear_status,
clear_song_ct = clear_song_ct,
song1_score = score1,
song2_score = score2,
song3_score = score3,
user=user_id,
version=version,
stage_id=stage_id,
clear_status=clear_status,
clear_song_ct=clear_song_ct,
song1_score=score1,
song2_score=score2,
song3_score=score3,
)
conflict = sql.on_duplicate_key_update(
clear_status = clear_status,
clear_song_ct = clear_song_ct,
song1_score = score1,
song2_score = score2,
song3_score = score3,
play_ct = stageup.c.play_ct + 1
clear_status=clear_status,
clear_song_ct=clear_song_ct,
song1_score=score1,
song2_score=score2,
song3_score=score3,
play_ct=stageup.c.play_ct + 1,
)
result = self.execute(conflict)
if result is None:
self.logger.warn(f"put_stageup: failed to update! user_id: {user_id} version: {version} stage_id: {stage_id}")
self.logger.warn(
f"put_stageup: failed to update! user_id: {user_id} version: {version} stage_id: {stage_id}"
)
return None
return result.lastrowid
def get_stageup(self, user_id: int, version: int) -> Optional[List[Row]]:
sql = select(stageup).where(and_(stageup.c.user==user_id, stageup.c.version==version))
sql = select(stageup).where(
and_(stageup.c.user == user_id, stageup.c.version == version)
)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchall()
def get_stageup_stage(self, user_id: int, version: int, stage_id: int) -> Optional[Row]:
def get_stageup_stage(
self, user_id: int, version: int, stage_id: int
) -> Optional[Row]:
sql = select(stageup).where(
and_(
stageup.c.user == user_id,
@@ -256,5 +322,6 @@ class WaccaScoreData(BaseData):
)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchone()

View File

@@ -23,46 +23,62 @@ music = Table(
Column("chartDesigner", String(255)),
Column("jacketFile", String(255)),
UniqueConstraint("version", "songId", "chartId", name="wacca_static_music_uk"),
mysql_charset='utf8mb4'
mysql_charset="utf8mb4",
)
class WaccaStaticData(BaseData):
def put_music(self, version: int, song_id: int, chart_id: int, title: str, artist: str, bpm: str,
difficulty: float, chart_designer: str, jacket: str) -> Optional[int]:
def put_music(
self,
version: int,
song_id: int,
chart_id: int,
title: str,
artist: str,
bpm: str,
difficulty: float,
chart_designer: str,
jacket: str,
) -> Optional[int]:
sql = insert(music).values(
version = version,
songId = song_id,
chartId = chart_id,
title = title,
artist = artist,
bpm = bpm,
difficulty = difficulty,
chartDesigner = chart_designer,
jacketFile = jacket
version=version,
songId=song_id,
chartId=chart_id,
title=title,
artist=artist,
bpm=bpm,
difficulty=difficulty,
chartDesigner=chart_designer,
jacketFile=jacket,
)
conflict = sql.on_duplicate_key_update(
title = title,
artist = artist,
bpm = bpm,
difficulty = difficulty,
chartDesigner = chart_designer,
jacketFile = jacket
title=title,
artist=artist,
bpm=bpm,
difficulty=difficulty,
chartDesigner=chart_designer,
jacketFile=jacket,
)
result = self.execute(conflict)
if result is None:
if result is None:
self.logger.warn(f"Failed to insert music {song_id} chart {chart_id}")
return None
return result.lastrowid
def get_music_chart(self, version: int, song_id: int, chart_id: int) -> Optional[List[Row]]:
sql = select(music).where(and_(
music.c.version == version,
music.c.songId == song_id,
music.c.chartId == chart_id
))
def get_music_chart(
self, version: int, song_id: int, chart_id: int
) -> Optional[List[Row]]:
sql = select(music).where(
and_(
music.c.version == version,
music.c.songId == song_id,
music.c.chartId == chart_id,
)
)
result = self.execute(sql)
if result is None: return None
if result is None:
return None
return result.fetchone()