Merge pull request 'add opt static tables' (#2) from Hay1tsme/artemis:develop into develop
Reviewed-on: https://gitea.tendokyu.moe/SoulGateKey/artemis/pulls/2pull/205/head
commit
4cb120dbb6
|
@ -0,0 +1,164 @@
|
|||
"""acc_opt_tables
|
||||
|
||||
Revision ID: 263884e774cc
|
||||
Revises: 1d0014d35220
|
||||
Create Date: 2025-04-07 18:05:53.349320
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '263884e774cc'
|
||||
down_revision = '1d0014d35220'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('chuni_static_opt',
|
||||
sa.Column('id', sa.BIGINT(), nullable=False),
|
||||
sa.Column('version', sa.INTEGER(), nullable=False),
|
||||
sa.Column('name', sa.VARCHAR(length=4), nullable=False),
|
||||
sa.Column('sequence', sa.INTEGER(), nullable=False),
|
||||
sa.Column('whenRead', sa.TIMESTAMP(), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('isEnable', sa.BOOLEAN(), server_default='1', nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('version', 'name', name='chuni_static_opt_uk'),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
op.create_table('cm_static_opts',
|
||||
sa.Column('id', sa.BIGINT(), nullable=False),
|
||||
sa.Column('version', sa.INTEGER(), nullable=False),
|
||||
sa.Column('name', sa.VARCHAR(length=4), nullable=False),
|
||||
sa.Column('sequence', sa.INTEGER(), nullable=True),
|
||||
sa.Column('gekiVersion', sa.INTEGER(), nullable=True),
|
||||
sa.Column('gekiReleaseVer', sa.INTEGER(), nullable=True),
|
||||
sa.Column('maiVersion', sa.INTEGER(), nullable=True),
|
||||
sa.Column('maiReleaseVer', sa.INTEGER(), nullable=True),
|
||||
sa.Column('whenRead', sa.TIMESTAMP(), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('isEnable', sa.BOOLEAN(), server_default='1', nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('version', 'name', name='cm_static_opts_uk'),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
op.create_table('mai2_static_opt',
|
||||
sa.Column('id', sa.BIGINT(), nullable=False),
|
||||
sa.Column('version', sa.INTEGER(), nullable=False),
|
||||
sa.Column('name', sa.VARCHAR(length=4), nullable=False),
|
||||
sa.Column('sequence', sa.INTEGER(), nullable=False),
|
||||
sa.Column('cmReleaseVer', sa.INTEGER(), nullable=False),
|
||||
sa.Column('whenRead', sa.TIMESTAMP(), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('isEnable', sa.BOOLEAN(), server_default='1', nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('version', 'name', name='mai2_static_opt_uk'),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
op.create_table('ongeki_static_opt',
|
||||
sa.Column('id', sa.BIGINT(), nullable=False),
|
||||
sa.Column('version', sa.INTEGER(), nullable=False),
|
||||
sa.Column('name', sa.VARCHAR(length=4), nullable=False),
|
||||
sa.Column('sequence', sa.INTEGER(), nullable=False),
|
||||
sa.Column('cmReleaseVer', sa.INTEGER(), nullable=False),
|
||||
sa.Column('whenRead', sa.TIMESTAMP(), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('isEnable', sa.BOOLEAN(), server_default='1', nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('version', 'name', name='ongeki_static_opt_uk'),
|
||||
mysql_charset='utf8mb4'
|
||||
)
|
||||
op.add_column('chuni_static_avatar', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_avatar', 'chuni_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('chuni_static_cards', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_cards', 'cm_static_opts', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('chuni_static_character', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_character', 'chuni_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('chuni_static_charge', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_charge', 'chuni_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('chuni_static_events', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_events', 'chuni_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('chuni_static_gachas', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_gachas', 'cm_static_opts', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('chuni_static_login_bonus', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_login_bonus', 'chuni_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('chuni_static_login_bonus_preset', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_login_bonus_preset', 'chuni_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('chuni_static_map_icon', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_map_icon', 'chuni_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('chuni_static_music', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_music', 'chuni_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('chuni_static_system_voice', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_system_voice', 'chuni_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('chuni_static_trophy', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'chuni_static_trophy', 'chuni_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('mai2_static_cards', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'mai2_static_cards', 'cm_static_opts', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('mai2_static_event', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'mai2_static_event', 'mai2_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('mai2_static_music', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'mai2_static_music', 'mai2_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('mai2_static_ticket', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'mai2_static_ticket', 'mai2_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('ongeki_static_cards', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'ongeki_static_cards', 'ongeki_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('ongeki_static_events', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'ongeki_static_events', 'ongeki_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('ongeki_static_gachas', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'ongeki_static_gachas', 'cm_static_opts', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('ongeki_static_music', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'ongeki_static_music', 'ongeki_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
op.add_column('ongeki_static_rewards', sa.Column('opt', sa.BIGINT(), nullable=True))
|
||||
op.create_foreign_key(None, 'ongeki_static_rewards', 'ongeki_static_opt', ['opt'], ['id'], onupdate='cascade', ondelete='SET NULL')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint("ongeki_static_rewards_ibfk_1", 'ongeki_static_rewards', type_='foreignkey')
|
||||
op.drop_column('ongeki_static_rewards', 'opt')
|
||||
op.drop_constraint("ongeki_static_music_ibfk_1", 'ongeki_static_music', type_='foreignkey')
|
||||
op.drop_column('ongeki_static_music', 'opt')
|
||||
op.drop_constraint("ongeki_static_gachas_ibfk_1", 'ongeki_static_gachas', type_='foreignkey')
|
||||
op.drop_column('ongeki_static_gachas', 'opt')
|
||||
op.drop_constraint("ongeki_static_events_ibfk_1", "ongeki_static_events", type_='foreignkey')
|
||||
op.drop_column('ongeki_static_events', 'opt')
|
||||
op.drop_constraint("ongeki_static_cards_ibfk_1", "ongeki_static_cards", type_='foreignkey')
|
||||
op.drop_column('ongeki_static_cards', 'opt')
|
||||
op.drop_constraint("mai2_static_ticket_ibfk_1", "mai2_static_ticket", type_='foreignkey')
|
||||
op.drop_column('mai2_static_ticket', 'opt')
|
||||
op.drop_constraint("mai2_static_music_ibfk_1", "mai2_static_music", type_='foreignkey')
|
||||
op.drop_column('mai2_static_music', 'opt')
|
||||
op.drop_constraint("mai2_static_event_ibfk_1", "mai2_static_event", type_='foreignkey')
|
||||
op.drop_column('mai2_static_event', 'opt')
|
||||
op.drop_constraint("mai2_static_cards_ibfk_1", "mai2_static_cards", type_='foreignkey')
|
||||
op.drop_column('mai2_static_cards', 'opt')
|
||||
op.drop_constraint("chuni_static_trophy_ibfk_1", "chuni_static_trophy", type_='foreignkey')
|
||||
op.drop_column('chuni_static_trophy', 'opt')
|
||||
op.drop_constraint("chuni_static_system_voice_ibfk_1", "chuni_static_system_voice", type_='foreignkey')
|
||||
op.drop_column('chuni_static_system_voice', 'opt')
|
||||
op.drop_constraint("chuni_static_music_ibfk_1", "chuni_static_music", type_='foreignkey')
|
||||
op.drop_column('chuni_static_music', 'opt')
|
||||
op.drop_constraint("chuni_static_map_icon_ibfk_1", "chuni_static_map_icon", type_='foreignkey')
|
||||
op.drop_column('chuni_static_map_icon', 'opt')
|
||||
op.drop_constraint("chuni_static_login_bonus_preset_ibfk_1", "chuni_static_login_bonus_preset", type_='foreignkey')
|
||||
op.drop_column('chuni_static_login_bonus_preset', 'opt')
|
||||
op.drop_constraint("chuni_static_login_bonus_ibfk_2", "chuni_static_login_bonus", type_='foreignkey')
|
||||
op.drop_column('chuni_static_login_bonus', 'opt')
|
||||
op.drop_constraint("chuni_static_gachas_ibfk_1", "chuni_static_gachas", type_='foreignkey')
|
||||
op.drop_column('chuni_static_gachas', 'opt')
|
||||
op.drop_constraint("chuni_static_events_ibfk_1", "chuni_static_events", type_='foreignkey')
|
||||
op.drop_column('chuni_static_events', 'opt')
|
||||
op.drop_constraint("chuni_static_charge_ibfk_1", "chuni_static_charge", type_='foreignkey')
|
||||
op.drop_column('chuni_static_charge', 'opt')
|
||||
op.drop_constraint("chuni_static_character_ibfk_1", "chuni_static_character", type_='foreignkey')
|
||||
op.drop_column('chuni_static_character', 'opt')
|
||||
op.drop_constraint("chuni_static_cards_ibfk_1", "chuni_static_cards", type_='foreignkey')
|
||||
op.drop_column('chuni_static_cards', 'opt')
|
||||
op.drop_constraint("chuni_static_avatar_ibfk_1", "chuni_static_avatar", type_='foreignkey')
|
||||
op.drop_column('chuni_static_avatar', 'opt')
|
||||
op.drop_table('ongeki_static_opt')
|
||||
op.drop_table('mai2_static_opt')
|
||||
op.drop_table('cm_static_opts')
|
||||
op.drop_table('chuni_static_opt')
|
||||
# ### end Alembic commands ###
|
|
@ -5,6 +5,7 @@ from datetime import datetime, timezone
|
|||
from os import walk
|
||||
from types import ModuleType
|
||||
from typing import Any, Dict, Optional
|
||||
import math
|
||||
|
||||
import jwt
|
||||
from starlette.requests import Request
|
||||
|
@ -92,6 +93,8 @@ class Utils:
|
|||
|
||||
return cls.real_title_port_ssl
|
||||
|
||||
def floor_to_nearest_005(version: int) -> int:
|
||||
return (version // 5) * 5
|
||||
|
||||
def create_sega_auth_key(
|
||||
aime_id: int,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from enum import Enum, IntEnum
|
||||
|
||||
from typing import Optional
|
||||
from core.utils import floor_to_nearest_005
|
||||
|
||||
class ChuniConstants:
|
||||
GAME_CODE = "SDBT"
|
||||
|
@ -78,10 +79,34 @@ class ChuniConstants:
|
|||
( 0, "D"),
|
||||
]
|
||||
|
||||
VERSION_LUT = {
|
||||
"100": VER_CHUNITHM,
|
||||
"105": VER_CHUNITHM_PLUS,
|
||||
"110": VER_CHUNITHM_AIR,
|
||||
"115": VER_CHUNITHM_AIR_PLUS,
|
||||
"120": VER_CHUNITHM_STAR,
|
||||
"125": VER_CHUNITHM_STAR_PLUS,
|
||||
"130": VER_CHUNITHM_AMAZON,
|
||||
"135": VER_CHUNITHM_AMAZON_PLUS,
|
||||
"140": VER_CHUNITHM_CRYSTAL,
|
||||
"145": VER_CHUNITHM_CRYSTAL_PLUS,
|
||||
"150": VER_CHUNITHM_PARADISE,
|
||||
"200": VER_CHUNITHM_NEW,
|
||||
"205": VER_CHUNITHM_NEW_PLUS,
|
||||
"210": VER_CHUNITHM_SUN,
|
||||
"215": VER_CHUNITHM_SUN_PLUS,
|
||||
"220": VER_CHUNITHM_LUMINOUS,
|
||||
"225": VER_CHUNITHM_LUMINOUS_PLUS,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def game_ver_to_string(cls, ver: int):
|
||||
return cls.VERSION_NAMES[ver]
|
||||
|
||||
@classmethod
|
||||
def int_ver_to_game_ver(cls, ver: int) -> Optional[int]:
|
||||
""" Takes an int ver (ex 100 for 1.00) and returns an internal game version """
|
||||
return cls.VERSION_LUT.get(str(floor_to_nearest_005(ver)), None)
|
||||
|
||||
class MapAreaConditionType(IntEnum):
|
||||
"""Condition types for the GetGameMapAreaConditionApi endpoint. Incomplete.
|
||||
|
|
|
@ -3,6 +3,7 @@ from os import walk, path
|
|||
import xml.etree.ElementTree as ET
|
||||
from read import BaseReader
|
||||
from PIL import Image
|
||||
import configparser
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.chuni.database import ChuniData
|
||||
|
@ -50,18 +51,19 @@ class ChuniReader(BaseReader):
|
|||
|
||||
for dir in data_dirs:
|
||||
self.logger.info(f"Read from {dir}")
|
||||
await self.read_events(f"{dir}/event")
|
||||
await self.read_music(f"{dir}/music", we_diff)
|
||||
await self.read_charges(f"{dir}/chargeItem")
|
||||
await self.read_avatar(f"{dir}/avatarAccessory")
|
||||
await self.read_login_bonus(f"{dir}/")
|
||||
await self.read_nameplate(f"{dir}/namePlate")
|
||||
await self.read_trophy(f"{dir}/trophy")
|
||||
await self.read_character(f"{dir}/chara", dds_images)
|
||||
await self.read_map_icon(f"{dir}/mapIcon")
|
||||
await self.read_system_voice(f"{dir}/systemVoice")
|
||||
this_opt_id = await self.read_opt_info(dir) # this also treats A000 as an opt, which is intended
|
||||
await self.read_events(f"{dir}/event", this_opt_id)
|
||||
await self.read_music(f"{dir}/music", we_diff, this_opt_id)
|
||||
await self.read_charges(f"{dir}/chargeItem", this_opt_id)
|
||||
await self.read_avatar(f"{dir}/avatarAccessory", this_opt_id)
|
||||
await self.read_login_bonus(f"{dir}/", this_opt_id)
|
||||
await self.read_nameplate(f"{dir}/namePlate", this_opt_id)
|
||||
await self.read_trophy(f"{dir}/trophy", this_opt_id)
|
||||
await self.read_character(f"{dir}/chara", dds_images, this_opt_id)
|
||||
await self.read_map_icon(f"{dir}/mapIcon", this_opt_id)
|
||||
await self.read_system_voice(f"{dir}/systemVoice", this_opt_id)
|
||||
|
||||
async def read_login_bonus(self, root_dir: str) -> None:
|
||||
async def read_login_bonus(self, root_dir: str, opt_id: Optional[int] = None) -> None:
|
||||
for root, dirs, files in walk(f"{root_dir}loginBonusPreset"):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/LoginBonusPreset.xml"):
|
||||
|
@ -132,7 +134,7 @@ class ChuniReader(BaseReader):
|
|||
f"Failed to insert login bonus {bonus_id}"
|
||||
)
|
||||
|
||||
async def read_events(self, evt_dir: str) -> None:
|
||||
async def read_events(self, evt_dir: str, opt_id: Optional[int] = None) -> None:
|
||||
for root, dirs, files in walk(evt_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/Event.xml"):
|
||||
|
@ -154,7 +156,7 @@ class ChuniReader(BaseReader):
|
|||
else:
|
||||
self.logger.warning(f"Failed to insert event {id}")
|
||||
|
||||
async def read_music(self, music_dir: str, we_diff: str = "4") -> None:
|
||||
async def read_music(self, music_dir: str, we_diff: str = "4", opt_id: Optional[int] = None) -> None:
|
||||
max_title_len = MusicTable.columns["title"].type.length
|
||||
max_artist_len = MusicTable.columns["artist"].type.length
|
||||
|
||||
|
@ -230,7 +232,7 @@ class ChuniReader(BaseReader):
|
|||
f"Failed to insert music {song_id} chart {chart_id}"
|
||||
)
|
||||
|
||||
async def read_charges(self, charge_dir: str) -> None:
|
||||
async def read_charges(self, charge_dir: str, opt_id: Optional[int] = None) -> None:
|
||||
for root, dirs, files in walk(charge_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/ChargeItem.xml"):
|
||||
|
@ -259,7 +261,7 @@ class ChuniReader(BaseReader):
|
|||
else:
|
||||
self.logger.warning(f"Failed to insert charge {id}")
|
||||
|
||||
async def read_avatar(self, avatar_dir: str) -> None:
|
||||
async def read_avatar(self, avatar_dir: str, opt_id: Optional[int] = None) -> None:
|
||||
for root, dirs, files in walk(avatar_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/AvatarAccessory.xml"):
|
||||
|
@ -292,7 +294,7 @@ class ChuniReader(BaseReader):
|
|||
else:
|
||||
self.logger.warning(f"Failed to insert avatarAccessory {id}")
|
||||
|
||||
async def read_nameplate(self, nameplate_dir: str) -> None:
|
||||
async def read_nameplate(self, nameplate_dir: str, opt_id: Optional[int] = None) -> None:
|
||||
for root, dirs, files in walk(nameplate_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/NamePlate.xml"):
|
||||
|
@ -321,7 +323,7 @@ class ChuniReader(BaseReader):
|
|||
else:
|
||||
self.logger.warning(f"Failed to insert nameplate {id}")
|
||||
|
||||
async def read_trophy(self, trophy_dir: str) -> None:
|
||||
async def read_trophy(self, trophy_dir: str, opt_id: Optional[int] = None) -> None:
|
||||
for root, dirs, files in walk(trophy_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/Trophy.xml"):
|
||||
|
@ -346,7 +348,7 @@ class ChuniReader(BaseReader):
|
|||
else:
|
||||
self.logger.warning(f"Failed to insert trophy {id}")
|
||||
|
||||
async def read_character(self, chara_dir: str, dds_images: dict) -> None:
|
||||
async def read_character(self, chara_dir: str, dds_images: dict, opt_id: Optional[int] = None) -> None:
|
||||
for root, dirs, files in walk(chara_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/Chara.xml"):
|
||||
|
@ -390,7 +392,7 @@ class ChuniReader(BaseReader):
|
|||
else:
|
||||
self.logger.warning(f"Failed to insert character {id}")
|
||||
|
||||
async def read_map_icon(self, mapicon_dir: str) -> None:
|
||||
async def read_map_icon(self, mapicon_dir: str, opt_id: Optional[int] = None) -> None:
|
||||
for root, dirs, files in walk(mapicon_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/MapIcon.xml"):
|
||||
|
@ -418,7 +420,7 @@ class ChuniReader(BaseReader):
|
|||
else:
|
||||
self.logger.warning(f"Failed to map icon {id}")
|
||||
|
||||
async def read_system_voice(self, voice_dir: str) -> None:
|
||||
async def read_system_voice(self, voice_dir: str, opt_id: Optional[int] = None) -> None:
|
||||
for root, dirs, files in walk(voice_dir):
|
||||
for dir in dirs:
|
||||
if path.exists(f"{root}/{dir}/SystemVoice.xml"):
|
||||
|
@ -446,6 +448,49 @@ class ChuniReader(BaseReader):
|
|||
else:
|
||||
self.logger.warning(f"Failed to system voice {id}")
|
||||
|
||||
async def read_opt_info(self, directory: str) -> Optional[int]:
|
||||
if not path.exists(f"{directory}/data.conf"):
|
||||
self.logger.warning(f"{directory} does not contain data.conf, opt info will not be read")
|
||||
return None
|
||||
|
||||
data_config = configparser.ConfigParser()
|
||||
if not data_config.read(f"{directory}/data.conf", 'utf-8'):
|
||||
self.logger.warning(f"{directory}/data.conf failed to read or parse, opt info will not be read")
|
||||
return None
|
||||
|
||||
if 'Version' not in data_config:
|
||||
self.logger.warning(f"{directory}/data.conf contains no Version section, opt info will not be read")
|
||||
return None
|
||||
|
||||
if 'Name' not in data_config['Version']: # Probably not worth checking that the other sections exist
|
||||
self.logger.warning(f"{directory}/data.conf contains no Name item in the Version section, opt info will not be read")
|
||||
return None
|
||||
|
||||
if 'VerMajor' not in data_config['Version']: # Probably not worth checking that the other sections exist
|
||||
self.logger.warning(f"{directory}/data.conf contains no VerMajor item in the Version section, opt info will not be read")
|
||||
return None
|
||||
|
||||
if 'VerMinor' not in data_config['Version']: # Probably not worth checking that the other sections exist
|
||||
self.logger.warning(f"{directory}/data.conf contains no VerMinor item in the Version section, opt info will not be read")
|
||||
return None
|
||||
|
||||
if 'VerRelease' not in data_config['Version']: # Probably not worth checking that the other sections exist
|
||||
self.logger.warning(f"{directory}/data.conf contains no VerRelease item in the Version section, opt info will not be read")
|
||||
return None
|
||||
|
||||
opt_seq = data_config['Version']['VerRelease']
|
||||
opt_folder = path.basename(path.normpath(directory))
|
||||
opt_id = await self.data.static.get_opt_by_version_folder(self.version, opt_folder)
|
||||
|
||||
if not opt_id:
|
||||
opt_id = await self.data.static.put_opt(self.version, opt_folder, opt_seq)
|
||||
if not opt_id:
|
||||
self.logger.error(f"Failed to put opt folder info for {opt_folder}")
|
||||
return None
|
||||
|
||||
self.logger.info(f"Opt folder {opt_folder} (Database ID {opt_id}) contains {data_config['Version']['Name']} v{data_config['Version']['VerMajor']}.{data_config['Version']['VerMinor']}.{opt_seq}")
|
||||
return opt_id
|
||||
|
||||
def copy_image(self, filename: str, src_dir: str, dst_dir: str) -> None:
|
||||
# Convert the image to png so we can easily display it in the frontend
|
||||
file_src = path.join(src_dir, filename)
|
||||
|
|
|
@ -7,8 +7,7 @@ from sqlalchemy import (
|
|||
PrimaryKeyConstraint,
|
||||
and_,
|
||||
)
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, Float
|
||||
from sqlalchemy.engine.base import Connection
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, BIGINT, Float, INTEGER, VARCHAR, BOOLEAN
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select
|
||||
|
@ -17,6 +16,19 @@ from datetime import datetime
|
|||
|
||||
from core.data.schema import BaseData, metadata
|
||||
|
||||
opts = Table(
|
||||
"chuni_static_opt",
|
||||
metadata,
|
||||
Column("id", BIGINT, primary_key=True, nullable=False),
|
||||
Column("version", INTEGER, nullable=False),
|
||||
Column("name", VARCHAR(4), nullable=False), # Axxx
|
||||
Column("sequence", INTEGER, nullable=False), # VerRelease in data.conf
|
||||
Column("whenRead", TIMESTAMP, nullable=False, server_default=func.now()),
|
||||
Column("isEnable", BOOLEAN, nullable=False, server_default="1"),
|
||||
UniqueConstraint("version", "name", name="chuni_static_opt_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
events = Table(
|
||||
"chuni_static_events",
|
||||
metadata,
|
||||
|
@ -27,6 +39,7 @@ events = Table(
|
|||
Column("name", String(255)),
|
||||
Column("startDate", TIMESTAMP, server_default=func.now()),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
Column("opt", ForeignKey("chuni_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "eventId", name="chuni_static_events_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -44,6 +57,7 @@ music = Table(
|
|||
Column("genre", String(255)),
|
||||
Column("jacketPath", String(255)),
|
||||
Column("worldsEndTag", String(7)),
|
||||
Column("opt", ForeignKey("chuni_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "songId", "chartId", name="chuni_static_music_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -59,6 +73,7 @@ charge = Table(
|
|||
Column("consumeType", Integer),
|
||||
Column("sellingAppeal", Boolean),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
Column("opt", ForeignKey("chuni_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "chargeId", name="chuni_static_charge_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -76,6 +91,7 @@ avatar = Table(
|
|||
Column("isEnabled", Boolean, server_default="1"),
|
||||
Column("defaultHave", Boolean, server_default="0"),
|
||||
Column("sortName", String(255)),
|
||||
Column("opt", ForeignKey("chuni_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "avatarAccessoryId", name="chuni_static_avatar_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -110,6 +126,7 @@ character = Table(
|
|||
Column("imagePath3", String(255)),
|
||||
Column("isEnabled", Boolean, server_default="1"),
|
||||
Column("defaultHave", Boolean, server_default="0"),
|
||||
Column("opt", ForeignKey("chuni_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "characterId", name="chuni_static_character_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -124,6 +141,7 @@ trophy = Table(
|
|||
Column("rareType", Integer),
|
||||
Column("isEnabled", Boolean, server_default="1"),
|
||||
Column("defaultHave", Boolean, server_default="0"),
|
||||
Column("opt", ForeignKey("chuni_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "trophyId", name="chuni_static_trophy_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -139,6 +157,7 @@ map_icon = Table(
|
|||
Column("iconPath", String(255)),
|
||||
Column("isEnabled", Boolean, server_default="1"),
|
||||
Column("defaultHave", Boolean, server_default="0"),
|
||||
Column("opt", ForeignKey("chuni_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "mapIconId", name="chuni_static_mapicon_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -154,6 +173,7 @@ system_voice = Table(
|
|||
Column("imagePath", String(255)),
|
||||
Column("isEnabled", Boolean, server_default="1"),
|
||||
Column("defaultHave", Boolean, server_default="0"),
|
||||
Column("opt", ForeignKey("chuni_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "voiceId", name="chuni_static_systemvoice_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -175,6 +195,7 @@ gachas = Table(
|
|||
Column("endDate", TIMESTAMP, server_default="2038-01-01 00:00:00.0"),
|
||||
Column("noticeStartDate", TIMESTAMP, server_default="2018-01-01 00:00:00.0"),
|
||||
Column("noticeEndDate", TIMESTAMP, server_default="2038-01-01 00:00:00.0"),
|
||||
Column("opt", ForeignKey("cm_static_opts.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "gachaId", "gachaName", name="chuni_static_gachas_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -195,6 +216,7 @@ cards = Table(
|
|||
Column("combo", Integer, nullable=False),
|
||||
Column("chain", Integer, nullable=False),
|
||||
Column("skillName", String(255), nullable=False),
|
||||
Column("opt", ForeignKey("cm_static_opts.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "cardId", name="chuni_static_cards_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -219,6 +241,7 @@ login_bonus_preset = Table(
|
|||
Column("version", Integer, nullable=False),
|
||||
Column("presetName", String(255), nullable=False),
|
||||
Column("isEnabled", Boolean, server_default="1"),
|
||||
Column("opt", ForeignKey("chuni_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
PrimaryKeyConstraint(
|
||||
"presetId", "version", name="chuni_static_login_bonus_preset_pk"
|
||||
),
|
||||
|
@ -238,6 +261,7 @@ login_bonus = Table(
|
|||
Column("itemNum", Integer, nullable=False),
|
||||
Column("needLoginDayCount", Integer, nullable=False),
|
||||
Column("loginBonusCategoryType", Integer, nullable=False),
|
||||
Column("opt", BIGINT),
|
||||
UniqueConstraint(
|
||||
"version", "presetId", "loginBonusId", name="chuni_static_login_bonus_uk"
|
||||
),
|
||||
|
@ -251,10 +275,18 @@ login_bonus = Table(
|
|||
ondelete="CASCADE",
|
||||
name="chuni_static_login_bonus_ibfk_1",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["opt"],
|
||||
[
|
||||
"chuni_static_opt.id",
|
||||
],
|
||||
onupdate="SET NULL",
|
||||
ondelete="CASCADE",
|
||||
name="chuni_static_login_bonus_ibfk_2",
|
||||
),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class ChuniStaticData(BaseData):
|
||||
async def put_login_bonus(
|
||||
self,
|
||||
|
@ -327,17 +359,17 @@ class ChuniStaticData(BaseData):
|
|||
return result.fetchone()
|
||||
|
||||
async def put_login_bonus_preset(
|
||||
self, version: int, preset_id: int, preset_name: str, is_enabled: bool
|
||||
self, version: int, preset_id: int, preset_name: str, isEnabled: bool
|
||||
) -> Optional[int]:
|
||||
sql = insert(login_bonus_preset).values(
|
||||
presetId=preset_id,
|
||||
version=version,
|
||||
presetName=preset_name,
|
||||
isEnabled=is_enabled,
|
||||
isEnabled=isEnabled,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
presetName=preset_name, isEnabled=is_enabled
|
||||
presetName=preset_name, isEnabled=isEnabled
|
||||
)
|
||||
|
||||
result = await self.execute(conflict)
|
||||
|
@ -346,12 +378,12 @@ class ChuniStaticData(BaseData):
|
|||
return result.lastrowid
|
||||
|
||||
async def get_login_bonus_presets(
|
||||
self, version: int, is_enabled: bool = True
|
||||
self, version: int, isEnabled: bool = True
|
||||
) -> Optional[List[Row]]:
|
||||
sql = login_bonus_preset.select(
|
||||
and_(
|
||||
login_bonus_preset.c.version == version,
|
||||
login_bonus_preset.c.isEnabled == is_enabled,
|
||||
login_bonus_preset.c.isEnabled == isEnabled,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -542,7 +574,6 @@ class ChuniStaticData(BaseData):
|
|||
return None
|
||||
return result.fetchone()
|
||||
|
||||
|
||||
async def put_avatar(
|
||||
self,
|
||||
version: int,
|
||||
|
@ -926,4 +957,86 @@ class ChuniStaticData(BaseData):
|
|||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
return result.fetchone()
|
||||
|
||||
async def put_opt(self, version: int, folder: str, sequence: int) -> Optional[int]:
|
||||
sql = insert(opts).values(version=version, name=folder, sequence=sequence)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(sequence=sequence, whenRead=datetime.now())
|
||||
|
||||
result = await self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warning(f"Failed to insert opt! version {version} folder {folder} sequence {sequence}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def get_opt_by_version_folder(self, version: int, folder: str) -> Optional[Row]:
|
||||
result = await self.execute(opts.select(and_(
|
||||
opts.c.version == version,
|
||||
opts.c.name == folder,
|
||||
)))
|
||||
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def get_opt_by_version_sequence(self, version: int, sequence: str) -> Optional[Row]:
|
||||
result = await self.execute(opts.select(and_(
|
||||
opts.c.version == version,
|
||||
opts.c.sequence == sequence,
|
||||
)))
|
||||
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def get_opts_by_version(self, version: int) -> Optional[List[Row]]:
|
||||
result = await self.execute(opts.select(opts.c.version == version))
|
||||
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
async def get_opts_enabled_by_version(self, version: int) -> Optional[List[Row]]:
|
||||
result = await self.execute(opts.select(and_(
|
||||
opts.c.version == version,
|
||||
opts.c.isEnable == True,
|
||||
)))
|
||||
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
async def get_latest_enabled_opt_by_version(self, version: int) -> Optional[Row]:
|
||||
result = await self.execute(
|
||||
opts.select(and_(
|
||||
opts.c.version == version,
|
||||
opts.c.isEnable == True,
|
||||
)).order_by(opts.c.sequence.desc())
|
||||
)
|
||||
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def get_opts(self) -> Optional[List[Row]]:
|
||||
result = await self.execute(opts.select())
|
||||
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
async def get_opts(self) -> Optional[List[Row]]:
|
||||
result = await self.execute(opts.select())
|
||||
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
async def set_opt_enabled(self, opt_id: int, enabled: bool) -> bool:
|
||||
result = await self.execute(opts.update(opts.c.id == opt_id).values(isEnable=enabled))
|
||||
|
||||
if result is None:
|
||||
self.logger.error(f"Failed to set opt enabled status to {enabled} for opt {opt_id}")
|
||||
return False
|
||||
return True
|
||||
|
|
|
@ -325,3 +325,39 @@ class CardMakerReader(BaseReader):
|
|||
maxSelectPoint=max_select_point,
|
||||
)
|
||||
self.logger.info(f"Added ongeki gacha {gacha_id}")
|
||||
|
||||
async def read_opt(self, base_dir: str) -> None:
|
||||
self.logger.info(f"Reading opt data from {base_dir}...")
|
||||
cm_data_cfg = None
|
||||
cm_data_cfg_file = os.path.join(base_dir, "DataConfig.xml")
|
||||
|
||||
geki_data_cfg = None
|
||||
geki_data_cfg_file = os.path.join(base_dir, "GEKI", "DataConfig.xml")
|
||||
|
||||
mai2_data_cfg = None
|
||||
mai2_data_cfg_file = os.path.join(base_dir, "MAI", "DataConfig.xml")
|
||||
|
||||
if os.path.exists(cm_data_cfg_file):
|
||||
with open(cm_data_cfg_file, "r") as f:
|
||||
cm_data_cfg = ET.fromstring(f.read())
|
||||
else:
|
||||
self.logger.info(f"No DataConfig.xml in {base_dir}, sequence will be null")
|
||||
|
||||
if os.path.exists(geki_data_cfg_file):
|
||||
with open(geki_data_cfg_file, "r") as f:
|
||||
geki_data_cfg = ET.fromstring(f.read())
|
||||
else:
|
||||
self.logger.info(f"Cannot find {geki_data_cfg_file}, gekiVersion and gekiReleaseVer will be null")
|
||||
|
||||
if os.path.exists(mai2_data_cfg_file):
|
||||
with open(mai2_data_cfg_file, "r") as f:
|
||||
mai2_data_cfg = ET.fromstring(f.read())
|
||||
else:
|
||||
self.logger.info(f"Cannot find {mai2_data_cfg_file}, mai2Version and mai2ReleaseVer will be null")
|
||||
|
||||
cm_rel_ver = int(cm_data_cfg.find("DataConfig/version/release").text)
|
||||
|
||||
geki_rel_ver = int(geki_data_cfg.find("DataConfig/version/release").text)
|
||||
|
||||
mai2_rel_ver = int(mai2_data_cfg.find("DataConfig/version/release").text)
|
||||
mai2_db_ver = Mai2Constants.int_ver_to_game_ver(mai2_data_cfg.find("DataConfig/version/major").text + mai2_data_cfg.find("DataConfig/version/minor").text)
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
from typing import Optional
|
||||
from core.utils import floor_to_nearest_005
|
||||
|
||||
class Mai2Constants:
|
||||
GRADE = {
|
||||
"D": 0,
|
||||
|
@ -83,6 +86,46 @@ class Mai2Constants:
|
|||
"maimai DX BUDDiES PLUS"
|
||||
)
|
||||
|
||||
MAI_VERSION_LUT = {
|
||||
"100": VER_MAIMAI,
|
||||
"110": VER_MAIMAI_PLUS,
|
||||
"120": VER_MAIMAI_GREEN,
|
||||
"130": VER_MAIMAI_GREEN_PLUS,
|
||||
"140": VER_MAIMAI_ORANGE,
|
||||
"150": VER_MAIMAI_ORANGE_PLUS,
|
||||
"160": VER_MAIMAI_PINK,
|
||||
"170": VER_MAIMAI_PINK_PLUS,
|
||||
"180": VER_MAIMAI_MURASAKI,
|
||||
"185": VER_MAIMAI_MURASAKI_PLUS,
|
||||
"190": VER_MAIMAI_MILK,
|
||||
"195": VER_MAIMAI_MILK_PLUS,
|
||||
"197": VER_MAIMAI_FINALE,
|
||||
}
|
||||
|
||||
MAI2_VERSION_LUT = {
|
||||
"100": VER_MAIMAI_DX,
|
||||
"105": VER_MAIMAI_DX_PLUS,
|
||||
"110": VER_MAIMAI_DX_SPLASH,
|
||||
"115": VER_MAIMAI_DX_SPLASH_PLUS,
|
||||
"120": VER_MAIMAI_DX_UNIVERSE,
|
||||
"125": VER_MAIMAI_DX_UNIVERSE_PLUS,
|
||||
"130": VER_MAIMAI_DX_FESTIVAL,
|
||||
"135": VER_MAIMAI_DX_FESTIVAL_PLUS,
|
||||
"140": VER_MAIMAI_DX_BUDDIES,
|
||||
"145": VER_MAIMAI_DX_BUDDIES_PLUS,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def game_ver_to_string(cls, ver: int):
|
||||
""" Takes an internal game version (ex 13 for maimai DX) and returns a the full name of the version """
|
||||
return cls.VERSION_STRING[ver]
|
||||
|
||||
@classmethod
|
||||
def int_ver_to_game_ver(cls, ver: int, is_dx = True) -> Optional[int]:
|
||||
""" Takes an int ver (ex 100 for 1.00) and returns an internal game version """
|
||||
if is_dx:
|
||||
return cls.MAI2_VERSION_LUT.get(str(floor_to_nearest_005(ver)), None)
|
||||
else:
|
||||
if ver >= 197:
|
||||
return cls.VER_MAIMAI_FINALE
|
||||
return cls.MAI_VERSION_LUT.get(str(floor_to_nearest_005(ver)), None)
|
||||
|
|
|
@ -2,13 +2,27 @@ from core.data.schema.base import BaseData, metadata
|
|||
|
||||
from typing import Optional, Dict, List
|
||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, Float
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, BIGINT, Float, INTEGER, BOOLEAN, VARCHAR
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
from datetime import datetime
|
||||
|
||||
opts = Table(
|
||||
"mai2_static_opt",
|
||||
metadata,
|
||||
Column("id", BIGINT, primary_key=True, nullable=False),
|
||||
Column("version", INTEGER, nullable=False),
|
||||
Column("name", VARCHAR(4), nullable=False), # Axxx
|
||||
Column("sequence", INTEGER, nullable=False), # release in DataConfig.xml
|
||||
Column("cmReleaseVer", INTEGER, nullable=False),
|
||||
Column("whenRead", TIMESTAMP, nullable=False, server_default=func.now()),
|
||||
Column("isEnable", BOOLEAN, nullable=False, server_default="1"),
|
||||
UniqueConstraint("version", "name", name="mai2_static_opt_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
event = Table(
|
||||
"mai2_static_event",
|
||||
metadata,
|
||||
|
@ -19,6 +33,7 @@ event = Table(
|
|||
Column("name", String(255)),
|
||||
Column("startDate", TIMESTAMP, server_default=func.now()),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
Column("opt", ForeignKey("mai2_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "eventId", "type", name="mai2_static_event_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -37,6 +52,7 @@ music = Table(
|
|||
Column("addedVersion", String(255)),
|
||||
Column("difficulty", Float),
|
||||
Column("noteDesigner", String(255)),
|
||||
Column("opt", ForeignKey("mai2_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("songId", "chartId", "version", name="mai2_static_music_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -51,6 +67,7 @@ ticket = Table(
|
|||
Column("name", String(255)),
|
||||
Column("price", Integer, server_default="1"),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
Column("opt", ForeignKey("mai2_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "ticketId", name="mai2_static_ticket_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -67,6 +84,7 @@ cards = Table(
|
|||
Column("noticeStartDate", TIMESTAMP, server_default="2018-01-01 00:00:00.0"),
|
||||
Column("noticeEndDate", TIMESTAMP, server_default="2038-01-01 00:00:00.0"),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
Column("opt", ForeignKey("cm_static_opts.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "cardId", "cardName", name="mai2_static_cards_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Final, Dict
|
||||
from typing import Optional
|
||||
from enum import Enum
|
||||
|
||||
from core.utils import floor_to_nearest_005
|
||||
|
||||
class OngekiConstants:
|
||||
GAME_CODE = "SDDT"
|
||||
|
@ -106,6 +106,24 @@ class OngekiConstants:
|
|||
"O.N.G.E.K.I. bright MEMORY Act.3",
|
||||
)
|
||||
|
||||
VERSION_LUT = {
|
||||
"100": VER_ONGEKI,
|
||||
"105": VER_ONGEKI_PLUS,
|
||||
"110": VER_ONGEKI_SUMMER,
|
||||
"115": VER_ONGEKI_SUMMER_PLUS,
|
||||
"120": VER_ONGEKI_RED,
|
||||
"125": VER_ONGEKI_RED_PLUS,
|
||||
"130": VER_ONGEKI_BRIGHT,
|
||||
"135": VER_ONGEKI_BRIGHT_MEMORY,
|
||||
"140": VER_ONGEKI_BRIGHT_MEMORY,
|
||||
"145": VER_ONGEKI_BRIGHT_MEMORY_ACT3,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def game_ver_to_string(cls, ver: int):
|
||||
return cls.VERSION_NAMES[ver]
|
||||
|
||||
@classmethod
|
||||
def int_ver_to_game_ver(cls, ver: int) -> Optional[int]:
|
||||
""" Takes an int ver (ex 100 for 1.00) and returns an internal game version """
|
||||
return cls.VERSION_LUT.get(str(floor_to_nearest_005(ver)), None)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Dict, List, Optional
|
||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, Float
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, BIGINT, Float, INTEGER, VARCHAR, BOOLEAN
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.engine import Row
|
||||
|
@ -9,6 +9,37 @@ from sqlalchemy.dialects.mysql import insert
|
|||
from core.data.schema import BaseData, metadata
|
||||
from core.data.schema.arcade import machine
|
||||
|
||||
opts = Table(
|
||||
"ongeki_static_opt",
|
||||
metadata,
|
||||
Column("id", BIGINT, primary_key=True, nullable=False),
|
||||
Column("version", INTEGER, nullable=False),
|
||||
Column("name", VARCHAR(4), nullable=False), # Axxx
|
||||
Column("sequence", INTEGER, nullable=False), # release in DataConfig.xml
|
||||
Column("cmReleaseVer", INTEGER, nullable=False),
|
||||
Column("whenRead", TIMESTAMP, nullable=False, server_default=func.now()),
|
||||
Column("isEnable", BOOLEAN, nullable=False, server_default="1"),
|
||||
UniqueConstraint("version", "name", name="ongeki_static_opt_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
cm_opts = Table(
|
||||
"cm_static_opts",
|
||||
metadata,
|
||||
Column("id", BIGINT, primary_key=True, nullable=False),
|
||||
Column("version", INTEGER, nullable=False),
|
||||
Column("name", VARCHAR(4), nullable=False), # Axxx
|
||||
Column("sequence", INTEGER), # Not all opts have a DataConfig.xml
|
||||
Column("gekiVersion", INTEGER), # GEKI/DataConfig.xml
|
||||
Column("gekiReleaseVer", INTEGER), # GEKI/DataConfig.xml
|
||||
Column("maiVersion", INTEGER), # MAI/DataConfig.xml
|
||||
Column("maiReleaseVer", INTEGER), # MAI/DataConfig.xml
|
||||
Column("whenRead", TIMESTAMP, nullable=False, server_default=func.now()),
|
||||
Column("isEnable", BOOLEAN, nullable=False, server_default="1"),
|
||||
UniqueConstraint("version", "name", name="cm_static_opts_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
events = Table(
|
||||
"ongeki_static_events",
|
||||
metadata,
|
||||
|
@ -20,6 +51,7 @@ events = Table(
|
|||
Column("startDate", TIMESTAMP, server_default=func.now()),
|
||||
Column("endDate", TIMESTAMP, server_default=func.now()),
|
||||
Column("enabled", Boolean, server_default="1"),
|
||||
Column("opt", ForeignKey("ongeki_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "eventId", "type", name="ongeki_static_events_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -36,6 +68,7 @@ music = Table(
|
|||
Column("artist", String(255)),
|
||||
Column("genre", String(255)),
|
||||
Column("level", Float),
|
||||
Column("opt", ForeignKey("ongeki_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "songId", "chartId", name="ongeki_static_music_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -59,6 +92,7 @@ gachas = Table(
|
|||
Column("noticeStartDate", TIMESTAMP, server_default="2018-01-01 00:00:00.0"),
|
||||
Column("noticeEndDate", TIMESTAMP, server_default="2038-01-01 00:00:00.0"),
|
||||
Column("convertEndDate", TIMESTAMP, server_default="2038-01-01 00:00:00.0"),
|
||||
Column("opt", ForeignKey("cm_static_opts.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "gachaId", "gachaName", name="ongeki_static_gachas_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -94,6 +128,7 @@ cards = Table(
|
|||
Column("skillId", Integer, nullable=False),
|
||||
Column("choKaikaSkillId", Integer, nullable=False),
|
||||
Column("cardNumber", String(255)),
|
||||
Column("opt", ForeignKey("ongeki_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "cardId", name="ongeki_static_cards_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
@ -107,6 +142,7 @@ rewards = Table(
|
|||
Column("rewardname", String(255), nullable=False),
|
||||
Column("itemKind", Integer, nullable=False),
|
||||
Column("itemId", Integer, nullable=False),
|
||||
Column("opt", ForeignKey("ongeki_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
|
||||
UniqueConstraint("version", "rewardId", name="ongeki_static_rewards_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue