From 784ac544f070c8c620aa7c6523d585d5dd2e6ba3 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 16 Jun 2024 14:42:23 -0400 Subject: [PATCH 1/7] fix event log ordering, paging, add timestamp --- core/data/schema/base.py | 2 +- core/templates/sys/logs.jinja | 40 ++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/core/data/schema/base.py b/core/data/schema/base.py index e315964..d74198b 100644 --- a/core/data/schema/base.py +++ b/core/data/schema/base.py @@ -109,7 +109,7 @@ class BaseData: return result.lastrowid async def get_event_log(self, entries: int = 100) -> Optional[List[Row]]: - sql = event_log.select().limit(entries) + sql = event_log.select().order_by(event_log.c.id.desc()).limit(entries) result = await self.execute(sql) if result is None: diff --git a/core/templates/sys/logs.jinja b/core/templates/sys/logs.jinja index 3dfe531..9b7a987 100644 --- a/core/templates/sys/logs.jinja +++ b/core/templates/sys/logs.jinja @@ -6,6 +6,7 @@ Severity + Timestamp System Name User @@ -19,7 +20,7 @@ {% if events is not defined or events|length == 0 %} - No Events + No Events {% endif %} @@ -66,7 +67,11 @@ function update_tbl() { for (var i = 0; i < per_page; i++) { let off = (page * per_page) + i; - if (off >= TBL_DATA.length ) { + if (off >= TBL_DATA.length) { + if (page != 0) { + document.getElementById("btn_next").disabled = true; + document.getElementById("btn_prev").disabled = false; + } break; } @@ -99,56 +104,59 @@ function update_tbl() { row.classList.add("table-primary"); break; } + + var cell_ts = row.insertCell(1); + cell_ts.innerHTML = data.when_logged; - var cell_mod = row.insertCell(1); + var cell_mod = row.insertCell(2); cell_mod.innerHTML = data.system; - var cell_name = row.insertCell(2); + var cell_name = row.insertCell(3); cell_name.innerHTML = data.type; - var cell_usr = row.insertCell(3); + var cell_usr = row.insertCell(4); if (data.user == 'NONE') { cell_usr.innerHTML = "---"; } else { cell_usr.innerHTML = "" + data.user + ""; } - var cell_arcade = row.insertCell(4); + var cell_arcade = row.insertCell(5); if (data.arcade == 'NONE') { cell_arcade.innerHTML = "---"; } else { cell_arcade.innerHTML = "" + data.arcade + ""; } - var cell_machine = row.insertCell(5); + var cell_machine = row.insertCell(6); if (data.arcade == 'NONE') { cell_machine.innerHTML = "---"; } else { cell_machine.innerHTML = "" + data.machine + ""; } - var cell_game = row.insertCell(6); + var cell_game = row.insertCell(7); if (data.game == 'NONE') { cell_game.innerHTML = "---"; } else { cell_game.innerHTML = data.game; } - var cell_version = row.insertCell(7); + var cell_version = row.insertCell(8); if (data.version == 'NONE') { cell_version.innerHTML = "---"; } else { cell_version.innerHTML = data.version; } - var cell_msg = row.insertCell(8); + var cell_msg = row.insertCell(9); if (data.message == '') { cell_msg.innerHTML = "---"; } else { cell_msg.innerHTML = data.message; } - var cell_deets = row.insertCell(9); + var cell_deets = row.insertCell(10); if (data.details == '{}') { cell_deets.innerHTML = "---"; } else { @@ -160,9 +168,11 @@ function update_tbl() { function chg_page(num) { var max_page = TBL_DATA.length / per_page; + console.log(max_page); page = page + num; - if (page > max_page) { + + if (page > max_page && max_page >= 1) { page = max_page; document.getElementById("btn_next").disabled = true; document.getElementById("btn_prev").disabled = false; @@ -172,6 +182,12 @@ function chg_page(num) { document.getElementById("btn_next").disabled = false; document.getElementById("btn_prev").disabled = true; return; + } else if (page == 0) { + document.getElementById("btn_next").disabled = false; + document.getElementById("btn_prev").disabled = true; + } else { + document.getElementById("btn_next").disabled = false; + document.getElementById("btn_prev").disabled = false; } update_tbl(); From 68bf3843ec0515f9c956a62b9319ffe6667de939 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 16 Jun 2024 14:45:41 -0400 Subject: [PATCH 2/7] pokken: fix default title plate id --- titles/pokken/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/pokken/base.py b/titles/pokken/base.py index fe11e99..f864824 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -226,7 +226,7 @@ class PokkenBase: load_usr.rankmatch_success = profile_dict.get("rankmatch_success", 0) load_usr.beat_num = profile_dict.get("beat_num", 0) load_usr.title_text_id = profile_dict.get("title_text_id", 2) - load_usr.title_plate_id = profile_dict.get("title_plate_id", 1) + load_usr.title_plate_id = profile_dict.get("title_plate_id", 31) load_usr.title_decoration_id = profile_dict.get("title_decoration_id", 1) load_usr.navi_trainer = profile_dict.get("navi_trainer", 0) load_usr.navi_version_id = profile_dict.get("navi_version_id", 0) From 766912c51d3a35e9fc1d7ed8606bc39956cfcf65 Mon Sep 17 00:00:00 2001 From: zaphkito Date: Tue, 18 Jun 2024 03:13:32 +0000 Subject: [PATCH 3/7] ongeki: fix base version title work --- titles/ongeki/index.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/titles/ongeki/index.py b/titles/ongeki/index.py index d7f3e0e..f91bf8f 100644 --- a/titles/ongeki/index.py +++ b/titles/ongeki/index.py @@ -216,16 +216,20 @@ class OngekiServlet(BaseServlet): ) return Response(zlib.compress(b'{"stat": "0"}')) - try: - unzip = zlib.decompress(req_raw) + if version < 105: + # O.N.G.E.K.I base don't use zlib + req_data = req_raw + else: + try: + unzip = zlib.decompress(req_raw) + + except zlib.error as e: + self.logger.error( + f"Failed to decompress v{version} {endpoint} request -> {e}" + ) + return Response(zlib.compress(b'{"stat": "0"}')) - except zlib.error as e: - self.logger.error( - f"Failed to decompress v{version} {endpoint} request -> {e}" - ) - return Response(zlib.compress(b'{"stat": "0"}')) - - req_data = json.loads(unzip) + req_data = json.loads(unzip) self.logger.info( f"v{version} {endpoint} request from {client_ip}" @@ -251,9 +255,12 @@ class OngekiServlet(BaseServlet): self.logger.debug(f"Response {resp}") - zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")) + resp_raw = json.dumps(resp, ensure_ascii=False).encode("utf-8") + zipped = zlib.compress(resp_raw) if not encrtped or version < 120: + if version < 105: + return Response(resp_raw) return Response(zipped) padded = pad(zipped, 16) From 51f65f9293020a0cefd0520638688ae82de95691 Mon Sep 17 00:00:00 2001 From: zaphkito Date: Tue, 18 Jun 2024 04:15:40 +0000 Subject: [PATCH 4/7] ongeki: I forgot json load --- titles/ongeki/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/ongeki/index.py b/titles/ongeki/index.py index f91bf8f..3bd0e15 100644 --- a/titles/ongeki/index.py +++ b/titles/ongeki/index.py @@ -218,7 +218,7 @@ class OngekiServlet(BaseServlet): if version < 105: # O.N.G.E.K.I base don't use zlib - req_data = req_raw + req_data = json.loads(req_raw) else: try: unzip = zlib.decompress(req_raw) From 5378655c520d723ca3977bedbbf0d8f4359372f2 Mon Sep 17 00:00:00 2001 From: beerpsi Date: Thu, 20 Jun 2024 08:11:24 +0700 Subject: [PATCH 5/7] [chunithm] Support LUMINOUS --- changelog.md | 4 + .../b23f985100ba_chunithm_luminous.py | 87 +++++++ docs/game_specific_info.md | 1 + example_config/chuni.yaml | 3 + titles/chuni/const.py | 20 +- titles/chuni/index.py | 21 +- titles/chuni/luminous.py | 244 ++++++++++++++++++ titles/chuni/new.py | 2 + titles/chuni/schema/item.py | 93 +++++++ 9 files changed, 467 insertions(+), 8 deletions(-) create mode 100644 core/data/alembic/versions/b23f985100ba_chunithm_luminous.py create mode 100644 titles/chuni/luminous.py diff --git a/changelog.md b/changelog.md index 3f8c6ba..07f17c8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ # Changelog Documenting updates to ARTEMiS, to be updated every time the master branch is pushed to. +## 20240620 +### CHUNITHM ++ CHUNITHM LUMINOUS support + ## 20240530 ### DIVA + Fix reader for when dificulty is not a int diff --git a/core/data/alembic/versions/b23f985100ba_chunithm_luminous.py b/core/data/alembic/versions/b23f985100ba_chunithm_luminous.py new file mode 100644 index 0000000..dd52974 --- /dev/null +++ b/core/data/alembic/versions/b23f985100ba_chunithm_luminous.py @@ -0,0 +1,87 @@ +"""CHUNITHM LUMINOUS + +Revision ID: b23f985100ba +Revises: 3657efefc5a4 +Create Date: 2024-06-20 08:08:08.759261 + +""" +from alembic import op +from sqlalchemy import Column, Integer, Boolean, UniqueConstraint + + +# revision identifiers, used by Alembic. +revision = 'b23f985100ba' +down_revision = '3657efefc5a4' +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "chuni_profile_net_battle", + Column("id", Integer, primary_key=True, nullable=False), + Column("user", Integer, nullable=False), + Column("isRankUpChallengeFailed", Boolean), + Column("highestBattleRankId", Integer), + Column("battleIconId", Integer), + Column("battleIconNum", Integer), + Column("avatarEffectPoint", Integer), + mysql_charset="utf8mb4", + ) + op.create_foreign_key( + None, + "chuni_profile_net_battle", + "aime_user", + ["user"], + ["id"], + ondelete="cascade", + onupdate="cascade", + ) + + op.create_table( + "chuni_item_cmission", + Column("id", Integer, primary_key=True, nullable=False), + Column("user", Integer, nullable=False), + Column("missionId", Integer, nullable=False), + Column("point", Integer), + UniqueConstraint("user", "missionId", name="chuni_item_cmission_uk"), + mysql_charset="utf8mb4", + ) + op.create_foreign_key( + None, + "chuni_item_cmission", + "aime_user", + ["user"], + ["id"], + ondelete="cascade", + onupdate="cascade", + ) + + op.create_table( + "chuni_item_cmission_progress", + Column("id", Integer, primary_key=True, nullable=False), + Column("user", Integer, nullable=False), + Column("missionId", Integer, nullable=False), + Column("order", Integer), + Column("stage", Integer), + Column("progress", Integer), + UniqueConstraint( + "user", "missionId", "order", name="chuni_item_cmission_progress_uk" + ), + mysql_charset="utf8mb4", + ) + op.create_foreign_key( + None, + "chuni_item_cmission_progress", + "aime_user", + ["user"], + ["id"], + ondelete="cascade", + onupdate="cascade", + ) + + +def downgrade(): + op.drop_table("chuni_profile_net_battle") + op.drop_table("chuni_item_cmission") + op.drop_table("chuni_item_cmission_progress") diff --git a/docs/game_specific_info.md b/docs/game_specific_info.md index a8e63c5..53c076c 100644 --- a/docs/game_specific_info.md +++ b/docs/game_specific_info.md @@ -63,6 +63,7 @@ Games listed below have been tested and confirmed working. | 12 | CHUNITHM NEW PLUS!! | | 13 | CHUNITHM SUN | | 14 | CHUNITHM SUN PLUS | +| 15 | CHUNITHM LUMINOUS | ### Importer diff --git a/example_config/chuni.yaml b/example_config/chuni.yaml index 53da186..4855fa1 100644 --- a/example_config/chuni.yaml +++ b/example_config/chuni.yaml @@ -22,6 +22,9 @@ version: 14: rom: 2.15.00 data: 2.15.00 + 15: + rom: 2.20.00 + data: 2.20.00 crypto: encrypted_only: False diff --git a/titles/chuni/const.py b/titles/chuni/const.py index 3e83378..2b79582 100644 --- a/titles/chuni/const.py +++ b/titles/chuni/const.py @@ -1,3 +1,6 @@ +from enum import Enum + + class ChuniConstants: GAME_CODE = "SDBT" GAME_CODE_NEW = "SDHD" @@ -20,6 +23,7 @@ class ChuniConstants: VER_CHUNITHM_NEW_PLUS = 12 VER_CHUNITHM_SUN = 13 VER_CHUNITHM_SUN_PLUS = 14 + VER_CHUNITHM_LUMINOUS = 15 VERSION_NAMES = [ "CHUNITHM", "CHUNITHM PLUS", @@ -35,9 +39,21 @@ class ChuniConstants: "CHUNITHM NEW!!", "CHUNITHM NEW PLUS!!", "CHUNITHM SUN", - "CHUNITHM SUN PLUS" + "CHUNITHM SUN PLUS", + "CHUNITHM LUMINOUS", ] @classmethod def game_ver_to_string(cls, ver: int): - return cls.VERSION_NAMES[ver] \ No newline at end of file + return cls.VERSION_NAMES[ver] + + +class MapAreaConditionType(Enum): + UNLOCKED = "0" + MAP_AREA_CLEARED = "2" + TROPHY_OBTAINED = "3" + + +class MapAreaConditionLogicalOperator(Enum): + OR = "0" + AND = "1" diff --git a/titles/chuni/index.py b/titles/chuni/index.py index f0f1eac..39dd9ba 100644 --- a/titles/chuni/index.py +++ b/titles/chuni/index.py @@ -1,7 +1,8 @@ from starlette.requests import Request from starlette.routing import Route from starlette.responses import Response -import logging, coloredlogs +import logging +import coloredlogs from logging.handlers import TimedRotatingFileHandler import zlib import yaml @@ -34,6 +35,7 @@ from .new import ChuniNew from .newplus import ChuniNewPlus from .sun import ChuniSun from .sunplus import ChuniSunPlus +from .luminous import ChuniLuminous class ChuniServlet(BaseServlet): def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: @@ -61,6 +63,7 @@ class ChuniServlet(BaseServlet): ChuniNewPlus, ChuniSun, ChuniSunPlus, + ChuniLuminous, ] self.logger = logging.getLogger("chuni") @@ -103,7 +106,9 @@ class ChuniServlet(BaseServlet): for method in method_list: method_fixed = inflection.camelize(method)[6:-7] # number of iterations was changed to 70 in SUN and then to 36 - if version == ChuniConstants.VER_CHUNITHM_SUN_PLUS: + if version == ChuniConstants.VER_CHUNITHM_LUMINOUS: + iter_count = 8 + elif version == ChuniConstants.VER_CHUNITHM_SUN_PLUS: iter_count = 36 elif version == ChuniConstants.VER_CHUNITHM_SUN: iter_count = 70 @@ -195,8 +200,10 @@ class ChuniServlet(BaseServlet): internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS elif version >= 210 and version < 215: # SUN internal_ver = ChuniConstants.VER_CHUNITHM_SUN - elif version >= 215: # SUN PLUS + elif version >= 215 and version < 220: # SUN PLUS internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS + elif version >= 220: # LUMINOUS + internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS elif game_code == "SDGS": # Int if version < 110: # SUPERSTAR / SUPERSTAR PLUS internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE # SUPERSTAR / SUPERSTAR PLUS worked fine with it @@ -206,8 +213,10 @@ class ChuniServlet(BaseServlet): internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS elif version >= 120 and version < 125: # SUN internal_ver = ChuniConstants.VER_CHUNITHM_SUN - elif version >= 125: # SUN PLUS + elif version >= 125 and version < 130: # SUN PLUS internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS + elif version >= 130: # LUMINOUS + internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32: # If we get a 32 character long hex string, it's a hash and we're @@ -295,7 +304,7 @@ class ChuniServlet(BaseServlet): self.logger.error(f"Error handling v{version} method {endpoint} - {e}") return Response(zlib.compress(b'{"stat": "0"}')) - if resp == None: + if resp is None: resp = {"returnCode": 1} self.logger.debug(f"Response {resp}") @@ -313,4 +322,4 @@ class ChuniServlet(BaseServlet): bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]), ) - return Response(crypt.encrypt(padded)) \ No newline at end of file + return Response(crypt.encrypt(padded)) diff --git a/titles/chuni/luminous.py b/titles/chuni/luminous.py new file mode 100644 index 0000000..2ced55b --- /dev/null +++ b/titles/chuni/luminous.py @@ -0,0 +1,244 @@ +from typing import Dict + +from core.config import CoreConfig +from titles.chuni.sunplus import ChuniSunPlus +from titles.chuni.const import ChuniConstants, MapAreaConditionLogicalOperator, MapAreaConditionType +from titles.chuni.config import ChuniConfig + + +class ChuniLuminous(ChuniSunPlus): + def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: + super().__init__(core_cfg, game_cfg) + self.version = ChuniConstants.VER_CHUNITHM_LUMINOUS + + async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + user_data = await super().handle_cm_get_user_preview_api_request(data) + + # Does CARD MAKER 1.35 work this far up? + user_data["lastDataVersion"] = "2.20.00" + return user_data + + async def handle_get_user_c_mission_api_request(self, data: Dict) -> Dict: + user_id = data["userId"] + mission_id = data["missionId"] + + progress_list = [] + point = 0 + + mission_data = await self.data.item.get_cmission(user_id, mission_id) + progress_data = await self.data.item.get_cmission_progress(user_id, mission_id) + + if mission_data and progress_data: + point = mission_data["point"] + + for progress in progress_data: + progress_list.append( + { + "order": progress["order"], + "stage": progress["stage"], + "progress": progress["progress"], + } + ) + + return { + "userId": user_id, + "missionId": mission_id, + "point": point, + "userCMissionProgressList": progress_list, + } + + async def handle_get_user_net_battle_ranking_info_api_request(self, data: Dict) -> Dict: + user_id = data["userId"] + + net_battle = {} + net_battle_data = await self.data.profile.get_net_battle(user_id) + + if net_battle_data: + net_battle = { + "isRankUpChallengeFailed": net_battle_data["isRankUpChallengeFailed"], + "highestBattleRankId": net_battle_data["highestBattleRankId"], + "battleIconId": net_battle_data["battleIconId"], + "battleIconNum": net_battle_data["battleIconNum"], + "avatarEffectPoint": net_battle_data["avatarEffectPoint"], + } + + return { + "userId": user_id, + "userNetBattleData": net_battle, + } + + async def handle_get_game_map_area_condition_api_request(self, data: Dict) -> Dict: + # There is no game data for this, everything is server side. + # TODO: Figure out conditions for 1UM1N0US ep.111 + return { + "length": "7", + "gameMapAreaConditionList": [ + # Secret AREA: MUSIC GAME + { + "mapAreaId": "2206201", # BlythE ULTIMA + "length": "1", + # Obtain the trophy "MISSION in progress", which is only available + # when running the first CHUNITHM mission + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6832", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2024-01-25 02:00:00.0", + } + ], + }, + { + "mapAreaId": "2206202", # PRIVATE SERVICE ULTIMA + "length": "1", + # Obtain the trophy "MISSION in progress", which is only available + # when running the first CHUNITHM mission + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6832", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2024-01-25 02:00:00.0", + } + ], + }, + { + "mapAreaId": "2206203", # New York Back Raise + "length": "1", + # SS NightTheater's EXPERT chart and get the title + # "今宵、劇場に映し出される景色とは――――。" + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6833", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + }, + ], + }, + { + "mapAreaId": "2206204", # Spasmodic + "length": "2", + # - Get 1 miss on Random (any difficulty) and get the title "当たり待ち" + # - Get 1 miss on 花たちに希望を (any difficulty) and get the title "花たちに希望を" + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6834", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + }, + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6835", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + }, + ], + }, + { + "mapAreaId": "2206205", # ΩΩPARTS + "length": "2", + # - S Sage EXPERT to get the title "マターリ進行キボンヌ" + # - Equip this title and play cab-to-cab with another person with this title + # to get "マターリしようよ" + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6836", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + }, + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6837", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2024-01-25 02:00:00.0", + }, + ], + }, + { + "mapAreaId": "2206206", # Blow My Mind + "length": "1", + # SS on CHAOS EXPERT, Hydra EXPERT, Surive EXPERT and Jakarta PROGRESSION EXPERT + # to get the title "Can you hear me?" + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6838", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + }, + ], + }, + { + "mapAreaId": "2206207", # VALLIS-NERIA + "length": "6", + # Finish the 6 other areas + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.MAP_AREA_CLEARED.value, + "conditionId": str(x), + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + } + for x in range(2206201, 2206207) + ], + }, + # { + # "mapAreaId": "3229301", # Mystic Rainbow of LUMINOUS Area 1 + # "length": "1", + # # Unlocks when any of the mainline LUMINOUS maps are completed? + # "mapAreaConditionList": [ + # # TODO + # ] + # }, + # { + # "mapAreaId": "3229302", # Mystic Rainbow of LUMINOUS Area 2 + # "length": "5", + # # Unlocks when LUMINOUS ep. I is completed + # "mapAreaConditionList": [ + # { + # "type": MapAreaConditionType.MAP_AREA_CLEARED.value, + # "conditionId": str(x), + # "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + # "startDate": "2023-12-14 07:00:00.0", + # "endDate": "2099-12-31 00:00:00.0", + # } + # for x in range(3220101, 3220106) + # ] + # }, + # { + # "mapAreaId": "3229303", # Mystic Rainbow of LUMINOUS Area 3 + # "length": "5", + # # Unlocks when LUMINOUS ep. II is completed + # "mapAreaConditionList": [ + # { + # "type": MapAreaConditionType.MAP_AREA_CLEARED.value, + # "conditionId": str(x), + # "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + # "startDate": "2023-12-14 07:00:00.0", + # "endDate": "2099-12-31 00:00:00.0", + # } + # for x in range(3220201, 3220206) + # ] + # }, + # { + # "mapAreaId": "3229304", # Mystic Rainbow of LUMINOUS Area 4 + # "length": "5", + # # Unlocks when LUMINOUS ep. III is completed + # "mapAreaConditionList": [ + + # ] + # } + ], + } diff --git a/titles/chuni/new.py b/titles/chuni/new.py index 9709f00..2275a6e 100644 --- a/titles/chuni/new.py +++ b/titles/chuni/new.py @@ -32,6 +32,8 @@ class ChuniNew(ChuniBase): return "210" if self.version == ChuniConstants.VER_CHUNITHM_SUN_PLUS: return "215" + if self.version == ChuniConstants.VER_CHUNITHM_LUMINOUS: + return "220" async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: # use UTC time and convert it to JST time by adding +9 diff --git a/titles/chuni/schema/item.py b/titles/chuni/schema/item.py index 5077e14..2f386ef 100644 --- a/titles/chuni/schema/item.py +++ b/titles/chuni/schema/item.py @@ -243,6 +243,36 @@ matching = Table( mysql_charset="utf8mb4", ) +cmission = Table( + "chuni_item_cmission", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column( + "user", + ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), + nullable=False, + ), + Column("missionId", Integer, nullable=False), + Column("point", Integer), + UniqueConstraint("user", "missionId", name="chuni_item_cmission_uk"), + mysql_charset="utf8mb4", +) + +cmission_progress = Table( + "chuni_item_cmission_progress", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column("user", ForeignKey("aime_user.id", ondelete="cascade"), nullable=False), + Column("missionId", Integer, nullable=False), + Column("order", Integer), + Column("stage", Integer), + Column("progress", Integer), + UniqueConstraint( + "user", "missionId", "order", name="chuni_item_cmission_progress_uk" + ), + mysql_charset="utf8mb4", +) + class ChuniItemData(BaseData): async def get_oldest_free_matching(self, version: int) -> Optional[Row]: @@ -594,3 +624,66 @@ class ChuniItemData(BaseData): ) return None return result.lastrowid + + async def put_cmission_progress( + self, user_id: int, mission_id: int, progress_data: Dict + ) -> Optional[int]: + progress_data["user"] = user_id + progress_data["missionId"] = mission_id + + sql = insert(cmission_progress).values(**progress_data) + conflict = sql.on_duplicate_key_update(**progress_data) + result = await self.execute(conflict) + + if result is None: + return None + + return result.lastrowid + + async def get_cmission_progress( + self, user_id: int, mission_id: int + ) -> Optional[List[Row]]: + sql = cmission_progress.select( + and_( + cmission_progress.c.user == user_id, + cmission_progress.c.missionId == mission_id, + ) + ).order_by(cmission_progress.c.order.asc()) + result = await self.execute(sql) + + if result is None: + return None + + return result.fetchall() + + async def get_cmission(self, user_id: int, mission_id: int) -> Optional[Row]: + sql = cmission.select( + and_(cmission.c.user == user_id, cmission.c.missionId == mission_id) + ) + result = await self.execute(sql) + + if result is None: + return None + + return result.fetchone() + + async def put_cmission(self, user_id: int, mission_data: Dict) -> Optional[int]: + mission_data["user"] = user_id + + sql = insert(cmission).values(**mission_data) + conflict = sql.on_duplicate_key_update(**mission_data) + result = await self.execute(conflict) + + if result is None: + return None + + return result.lastrowid + + async def get_cmissions(self, user_id: int) -> Optional[List[Row]]: + sql = cmission.select(cmission.c.user == user_id) + result = await self.execute(sql) + + if result is None: + return None + + return result.fetchall() From a1d54efbace85066234e2ec90620947e0d086cad Mon Sep 17 00:00:00 2001 From: beerpsi Date: Thu, 20 Jun 2024 20:28:52 +0700 Subject: [PATCH 6/7] add upserts --- titles/chuni/base.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/titles/chuni/base.py b/titles/chuni/base.py index 9e8a634..2a662d7 100644 --- a/titles/chuni/base.py +++ b/titles/chuni/base.py @@ -941,6 +941,31 @@ class ChuniBase: rating_type, upsert[rating_type], ) + + # added in LUMINOUS + if "userCMissionList" in upsert: + for cmission in upsert["userCMissionList"]: + mission_id = cmission["missionId"] + + await self.data.item.put_cmission( + user_id, + { + "missionId": mission_id, + "point": cmission["point"], + }, + ) + + for progress in cmission["userCMissionProgressList"]: + await self.data.item.put_cmission_progress(user_id, mission_id, progress) + + if "userNetBattleData" in upsert: + net_battle = upsert["userNetBattleData"][0] + + # fix the boolean + net_battle["isRankUpChallengeFailed"] = ( + False if net_battle["isRankUpChallengeFailed"] == "false" else True + ) + await self.data.profile.put_net_battle(user_id, net_battle) return {"returnCode": "1"} @@ -969,4 +994,4 @@ class ChuniBase: return { "userId": data["userId"], "userNetBattleData": {"recentNBSelectMusicList": []}, - } \ No newline at end of file + } From a5b47e20959b6394972061d2989bb7fb22939b60 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 20 Jun 2024 10:59:26 -0400 Subject: [PATCH 7/7] wacca: fix vs song updates --- titles/wacca/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/titles/wacca/base.py b/titles/wacca/base.py index 8a37c2b..58b1ba9 100644 --- a/titles/wacca/base.py +++ b/titles/wacca/base.py @@ -833,14 +833,14 @@ class WaccaBase: # TODO: Coop and vs data async def handle_user_music_updateCoop_request(self, data: Dict) -> Dict: coop_info = data["params"][4] - return self.handle_user_music_update_request(data) + return await self.handle_user_music_update_request(data) async def handle_user_music_updateVersus_request(self, data: Dict) -> Dict: vs_info = data["params"][4] - return self.handle_user_music_update_request(data) + return await self.handle_user_music_update_request(data) async def handle_user_music_updateTrial_request(self, data: Dict) -> Dict: - return self.handle_user_music_update_request(data) + return await self.handle_user_music_update_request(data) async def handle_user_mission_update_request(self, data: Dict) -> Dict: req = UserMissionUpdateRequest(data)