From 6b0838062eff5e73402b1df3dcb4ec0569ccd077 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Wed, 1 Mar 2023 23:55:29 -0500 Subject: [PATCH 01/16] wacca: add lily to list of items given on profile create, fixes #4 --- titles/wacca/lily.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/titles/wacca/lily.py b/titles/wacca/lily.py index 66a4bc8..e67b25c 100644 --- a/titles/wacca/lily.py +++ b/titles/wacca/lily.py @@ -50,6 +50,14 @@ class WaccaLily(WaccaS): resp = HousingStartResponseV1(region_id) return resp.make() + + 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 + + return resp def handle_user_status_get_request(self, data: Dict)-> Dict: req = UserStatusGetRequest(data) From 5965362a0f612600840366cbf010643d6bb83d8c Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 2 Mar 2023 00:14:13 -0500 Subject: [PATCH 02/16] title: add 405 and 404 error responses --- core/title.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/title.py b/core/title.py index 6c54450..252a130 100644 --- a/core/title.py +++ b/core/title.py @@ -53,10 +53,13 @@ class TitleServlet(): code = endpoints["game"] if code not in self.title_registry: self.logger.warn(f"Unknown game code {code}") + request.setResponseCode(404) + return b"" index = self.title_registry[code] if not hasattr(index, "render_GET"): self.logger.warn(f"{code} does not dispatch GET") + request.setResponseCode(405) return b"" return index.render_GET(request, endpoints["version"], endpoints["endpoint"]) @@ -65,10 +68,13 @@ class TitleServlet(): code = endpoints["game"] if code not in self.title_registry: self.logger.warn(f"Unknown game code {code}") + request.setResponseCode(404) + return b"" index = self.title_registry[code] if not hasattr(index, "render_POST"): self.logger.warn(f"{code} does not dispatch POST") + request.setResponseCode(405) return b"" return index.render_POST(request, int(endpoints["version"]), endpoints["endpoint"]) From 7071ab0bd907f1c06c9b1dd771580913882220a2 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 2 Mar 2023 00:24:04 -0500 Subject: [PATCH 03/16] chuni: add IP logging, clean up logs --- titles/chuni/index.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/titles/chuni/index.py b/titles/chuni/index.py index 25f31b5..a0f8b55 100644 --- a/titles/chuni/index.py +++ b/titles/chuni/index.py @@ -135,7 +135,8 @@ class ChuniServlet(): req_data = json.loads(unzip) - self.logger.info(f"v{version} {endpoint} request - {req_data}") + self.logger.info(f"v{version} {endpoint} request from {request.getClientAddress().host}") + self.logger.debug(req_data) func_to_find = "handle_" + inflection.underscore(endpoint) + "_request" @@ -154,7 +155,7 @@ class ChuniServlet(): if resp == None: resp = {'returnCode': 1} - self.logger.info(f"Response {resp}") + self.logger.debug(f"Response {resp}") zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")) From 846a556c5bcafa2c879453f46327a31086601370 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 2 Mar 2023 10:28:49 -0500 Subject: [PATCH 04/16] fix nginx example --- example_config/nginx_example.conf | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/example_config/nginx_example.conf b/example_config/nginx_example.conf index 096a072..30c733c 100644 --- a/example_config/nginx_example.conf +++ b/example_config/nginx_example.conf @@ -99,26 +99,6 @@ server { } } -# CXB, comment this out if you don't plan on serving crossbeats. -server { - listen 443 ssl; - server_name cxb.hostname.here; - - ssl_certificate /path/to/cert/cxb.pem; - ssl_certificate_key /path/to/cert/cxb.key; - ssl_session_timeout 1d; - ssl_session_cache shared:MozSSL:10m; - ssl_session_tickets off; - - ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; - ssl_ciphers "ALL:@SECLEVEL=1"; - ssl_prefer_server_ciphers off; - - location / { - proxy_pass http://localhost:8080/SDBT/104/; - } -} - # Frontend, set to redirect to HTTPS. Comment out if you don't intend to use the frontend server { listen 80; From d5a7247a7f9d0fa87f51b1d53a0596bde9e4bc75 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 2 Mar 2023 11:47:40 -0500 Subject: [PATCH 05/16] add production config --- docs/prod.md | 35 +++++++++++++++++++++++++++++++ example_config/nginx_example.conf | 1 + 2 files changed, 36 insertions(+) create mode 100644 docs/prod.md diff --git a/docs/prod.md b/docs/prod.md new file mode 100644 index 0000000..2967679 --- /dev/null +++ b/docs/prod.md @@ -0,0 +1,35 @@ +# ARTEMiS Production mode +Production mode is a configuration option that changes how the server listens to be more friendly to a production environment. This mode assumes that a proxy (for this guide, nginx) is standing in front of the server to handle port mapping and TLS. In order to activate production mode, simply change `is_develop` to `False` in `core.yaml`. Next time you start the server, you should see "Starting server in production mode". + +## Nginx Configuration +### Port forwarding +Artemis requires that the following ports be forwarded to allow internet traffic to access the server. This will not change regardless of what you set in the config, as many of these ports are hard-coded in the games. +`tcp:80` all.net, non-ssl titles +`tcp:8443` billing +`tcp:22345` aimedb +`tcp:443` frontend, SSL titles + +### A note about external proxy services (cloudflare, etc) +Due to the way that artemis functions, it is currently not possible to put the server behind something like Cloudflare. Cloudflare only proxies web traffic on the standard ports (80, 443) and, as shown above, this does not work with artemis. Server administrators should seek other means to protect their network (VPS hosting, VPN, etc) + +### Port mappings +An example config is provided in the `config` folder called `nginx_example.conf`. It is set up for the following: +`naominet.jp:tcp:80` -> `localhost:tcp:8000` for allnet +`ib.naominet.jp:ssl:8443` -> `localhost:tcp:8444` for the billing server +`your.hostname.here:ssl:443` -> `localhost:tcp:8080` for the SSL title server +`your.hostname.here:tcp:80` -> `localhost:tcp:8080` for the non-SSL title server +`cxb.hostname.here:ssl:443` -> `localhost:tcp:8080` for crossbeats (appends /SDCA/104/ to the request) +`pokken.hostname.here:ssl:443` -> `localhost:tcp:8080` for pokken +`frontend.hostname.here:ssl:443` -> `localhost:tcp:8090` for the frontend, includes https redirection + +If you're using this as a guide, be sure to replace your.hostname.here with the hostname you specified in core.yaml under `titles->hostname`. Do *not* change naominet.jp, or allnet/billing will fail. Also remember to specifiy certificat paths correctly, as in the example they are simply placeholders. + +### Multi-service ports +It is possible to use nginx to redirect billing and title server requests to the same port that all.net uses. By setting `port` to 0 under billing and title server, you can change the nginx config to serve the following (entries not shown here should be the same) +`ib.naominet.jp:ssl:8443` -> `localhost:tcp:8000` for the billing server +`your.hostname.here:ssl:443` -> `localhost:tcp:8000` for the SSL title server +`your.hostname.here:tcp:80` -> `localhost:tcp:8000` for the non-SSL title server +`cxb.hostname.here:ssl:443` -> `localhost:tcp:8000` for crossbeats (appends /SDCA/104/ to the request) +`pokken.hostname.here:ssl:443` -> `localhost:tcp:8000` for pokken + +This will allow you to only use 3 ports locally, but you will still need to forward the same internet-facing ports as before. \ No newline at end of file diff --git a/example_config/nginx_example.conf b/example_config/nginx_example.conf index 30c733c..fe6f7a7 100644 --- a/example_config/nginx_example.conf +++ b/example_config/nginx_example.conf @@ -114,6 +114,7 @@ server { # Frontend HTTPS. Comment out if you on't intend to use the frontend server { listen 443 ssl; + server_name frontend.hostname.here; ssl_certificate /path/to/cert/frontend.pem; ssl_certificate_key /path/to/cert/frontend.key; From 99881ea2208defc8303c9ef090ef9d743901646e Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 2 Mar 2023 11:54:50 -0500 Subject: [PATCH 06/16] docs: Add note about SSL certs to prod.md --- docs/prod.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/prod.md b/docs/prod.md index 2967679..35785f2 100644 --- a/docs/prod.md +++ b/docs/prod.md @@ -12,6 +12,12 @@ Artemis requires that the following ports be forwarded to allow internet traffic ### A note about external proxy services (cloudflare, etc) Due to the way that artemis functions, it is currently not possible to put the server behind something like Cloudflare. Cloudflare only proxies web traffic on the standard ports (80, 443) and, as shown above, this does not work with artemis. Server administrators should seek other means to protect their network (VPS hosting, VPN, etc) +### SSL Certificates +You will need to generate SSL certificates for some games. The certificates vary in security and validity requirements. Please see the general guide below +- General Title: The certificate for the general title server should be valid, not self-signed and match the CN that the game will be reaching out to (e.i if your games are reaching out to titles.hostname.here, your ssl certificate should be valid for titles.hostname.here, or *.hostname.here) +- CXB: Same requires as the title server. It must not be self-signed, and CN must match. Recomended to get a wildcard cert if possible, and use it for both Title and CXB +- Pokken: Pokken can be self-signed, and the CN doesn't have to match, but it MUST use 2048-bit RSA. Due to the games age, andthing stronger then that will be rejected. + ### Port mappings An example config is provided in the `config` folder called `nginx_example.conf`. It is set up for the following: `naominet.jp:tcp:80` -> `localhost:tcp:8000` for allnet From 4d9ae19cb295c2e7f38194496d1a6bab503caf86 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 2 Mar 2023 11:59:52 -0500 Subject: [PATCH 07/16] update readme --- readme.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/readme.md b/readme.md index 50bf933..1e05d5a 100644 --- a/readme.md +++ b/readme.md @@ -29,10 +29,8 @@ Games listed below have been tested and confirmed working. Only game versions ol - memcached (for non-windows platforms) - mysql/mariadb server -## Quick start guide -1) Clone this repository -2) Install requirements (see the platform-specific guides for instructions) -3) Install python libraries via `pip` -4) Copy the example configuration files into another folder (by default the server looks for the `config` directory) -5) Edit the newly copied configuration files to your liking, using [this](docs/config.md) doc as a guide. -6) Run the server by invoking `index.py` ex. `python3 index.py` \ No newline at end of file +## Setup guides +Follow the platform-specific guides for [windows](docs/INSTALL_WINDOWS.md) and [ubuntu](docs/INSTALL_UBUNTU.md) to setup and run the server. + +## Production guide +See the [production guide](docs/prod.md) for running a production server. From e205777693e2e229ea7c7dcbb0fd5b819b3d47d0 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 2 Mar 2023 12:05:38 -0500 Subject: [PATCH 08/16] docs: fix typo in prod.md --- docs/prod.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/prod.md b/docs/prod.md index 35785f2..de79b99 100644 --- a/docs/prod.md +++ b/docs/prod.md @@ -28,7 +28,7 @@ An example config is provided in the `config` folder called `nginx_example.conf` `pokken.hostname.here:ssl:443` -> `localhost:tcp:8080` for pokken `frontend.hostname.here:ssl:443` -> `localhost:tcp:8090` for the frontend, includes https redirection -If you're using this as a guide, be sure to replace your.hostname.here with the hostname you specified in core.yaml under `titles->hostname`. Do *not* change naominet.jp, or allnet/billing will fail. Also remember to specifiy certificat paths correctly, as in the example they are simply placeholders. +If you're using this as a guide, be sure to replace your.hostname.here with the hostname you specified in core.yaml under `titles->hostname`. Do *not* change naominet.jp, or allnet/billing will fail. Also remember to specifiy certificate paths correctly, as in the example they are simply placeholders. ### Multi-service ports It is possible to use nginx to redirect billing and title server requests to the same port that all.net uses. By setting `port` to 0 under billing and title server, you can change the nginx config to serve the following (entries not shown here should be the same) From c5fc879af669251462d268eb4992a0044ec53b89 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 2 Mar 2023 13:02:43 -0500 Subject: [PATCH 09/16] core: add taiwan to AllnetCountryCode --- core/const.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core/const.py b/core/const.py index 3a2c116..da018ee 100644 --- a/core/const.py +++ b/core/const.py @@ -38,6 +38,7 @@ class AllnetCountryCode(Enum): HONG_KONG = "HKG" SINGAPORE = "SGP" SOUTH_KOREA = "KOR" + TAIWAN = "TWN" CHINA = "CHN" class AllnetJapanRegionId(Enum): From 937dba20cafdc4ab47d1b4aff686e84d223a8aec Mon Sep 17 00:00:00 2001 From: Midorica Date: Thu, 2 Mar 2023 22:13:18 -0500 Subject: [PATCH 10/16] Fixing billing that was failing for Chunithm --- core/allnet.py | 2 +- index.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/allnet.py b/core/allnet.py index c938a88..587aeab 100644 --- a/core/allnet.py +++ b/core/allnet.py @@ -191,7 +191,7 @@ class AllnetServlet: self.logger.debug(f"request {req_dict}") - rsa = RSA.import_key(open(self.config.billing.sign_key, 'rb').read()) + rsa = RSA.import_key(open(self.config.billing.signing_key, 'rb').read()) signer = PKCS1_v1_5.new(rsa) digest = SHA.new() diff --git a/index.py b/index.py index e514b10..51842bd 100644 --- a/index.py +++ b/index.py @@ -29,6 +29,7 @@ class HttpDispatcher(resource.Resource): self.map_post.connect('allnet_poweron', '/sys/servlet/PowerOn', controller="allnet", action='handle_poweron', conditions=dict(method=['POST'])) self.map_post.connect('allnet_downloadorder', '/sys/servlet/DownloadOrder', controller="allnet", action='handle_dlorder', conditions=dict(method=['POST'])) self.map_post.connect('allnet_billing', '/request', controller="allnet", action='handle_billing_request', conditions=dict(method=['POST'])) + self.map_post.connect('allnet_billing', '/request/', controller="allnet", action='handle_billing_request', conditions=dict(method=['POST'])) self.map_post.connect('mucha_boardauth', '/mucha/boardauth.do', controller="mucha", action='handle_boardauth', conditions=dict(method=['POST'])) self.map_post.connect('mucha_updatacheck', '/mucha/updatacheck.do', controller="mucha", action='handle_updatacheck', conditions=dict(method=['POST'])) From 3791b2b2383c9a13aa896b6cc821522d9f0a0ee4 Mon Sep 17 00:00:00 2001 From: Midorica Date: Fri, 3 Mar 2023 00:00:22 -0500 Subject: [PATCH 11/16] Adding Ongeki Bright Memory support --- core/data/schema/versions/SDDT_2_rollback.sql | 7 +++ core/data/schema/versions/SDDT_3_upgrade.sql | 27 +++++++++ readme.md | 2 +- titles/ongeki/base.py | 4 ++ titles/ongeki/brightmemory.py | 58 +++++++++++++++++++ titles/ongeki/const.py | 3 +- titles/ongeki/index.py | 6 +- titles/ongeki/schema/item.py | 40 +++++++++++++ titles/ongeki/schema/profile.py | 1 + titles/ongeki/schema/score.py | 3 + 10 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 core/data/schema/versions/SDDT_2_rollback.sql create mode 100644 core/data/schema/versions/SDDT_3_upgrade.sql create mode 100644 titles/ongeki/brightmemory.py diff --git a/core/data/schema/versions/SDDT_2_rollback.sql b/core/data/schema/versions/SDDT_2_rollback.sql new file mode 100644 index 0000000..5ddf1b6 --- /dev/null +++ b/core/data/schema/versions/SDDT_2_rollback.sql @@ -0,0 +1,7 @@ +SET FOREIGN_KEY_CHECKS=0; +ALTER TABLE ongeki_profile_data DROP COLUMN isDialogWatchedSuggestMemory; +ALTER TABLE ongeki_score_best DROP COLUMN platinumScoreMax; +ALTER TABLE ongeki_score_playlog DROP COLUMN platinumScore; +ALTER TABLE ongeki_score_playlog DROP COLUMN platinumScoreMax; +DROP TABLE IF EXISTS `ongeki_user_memorychapter`; +SET FOREIGN_KEY_CHECKS=1; diff --git a/core/data/schema/versions/SDDT_3_upgrade.sql b/core/data/schema/versions/SDDT_3_upgrade.sql new file mode 100644 index 0000000..26c5c30 --- /dev/null +++ b/core/data/schema/versions/SDDT_3_upgrade.sql @@ -0,0 +1,27 @@ +SET FOREIGN_KEY_CHECKS=0; + +ALTER TABLE ongeki_profile_data ADD COLUMN isDialogWatchedSuggestMemory BOOLEAN; +ALTER TABLE ongeki_score_best ADD COLUMN platinumScoreMax INTEGER; +ALTER TABLE ongeki_score_playlog ADD COLUMN platinumScore INTEGER; +ALTER TABLE ongeki_score_playlog ADD COLUMN platinumScoreMax INTEGER; + +CREATE TABLE ongeki_user_memorychapter ( + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + user INT NOT NULL, + chapterId INT NOT NULL, + gaugeId INT NOT NULL, + gaugeNum INT NOT NULL, + jewelCount INT NOT NULL, + isStoryWatched BOOLEAN NOT NULL, + isBossWatched BOOLEAN NOT NULL, + isDialogWatched BOOLEAN NOT NULL, + isEndingWatched BOOLEAN NOT NULL, + isClear BOOLEAN NOT NULL, + lastPlayMusicId INT NOT NULL, + lastPlayMusicLevel INT NOT NULL, + lastPlayMusicCategory INT NOT NULL, + UNIQUE KEY ongeki_user_memorychapter_uk (user, chapterId), + CONSTRAINT ongeki_user_memorychapter_ibfk_1 FOREIGN KEY (user) REFERENCES aime_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +SET FOREIGN_KEY_CHECKS=1; diff --git a/readme.md b/readme.md index 1e05d5a..24440fc 100644 --- a/readme.md +++ b/readme.md @@ -16,7 +16,7 @@ Games listed below have been tested and confirmed working. Only game versions ol + All versions + Ongeki - + All versions up to Bright + + All versions up to Bright Memory + Wacca + Lily R diff --git a/titles/ongeki/base.py b/titles/ongeki/base.py index 8778645..3f6dc7a 100644 --- a/titles/ongeki/base.py +++ b/titles/ongeki/base.py @@ -787,6 +787,10 @@ class OngekiBase(): if "userChapterList" in upsert: for x in upsert["userChapterList"]: self.data.item.put_chapter(user_id, x) + + if "userMemoryChapterList" in upsert: + for x in upsert["userMemoryChapterList"]: + self.data.item.put_memorychapter(user_id, x) if "userItemList" in upsert: for x in upsert["userItemList"]: diff --git a/titles/ongeki/brightmemory.py b/titles/ongeki/brightmemory.py new file mode 100644 index 0000000..a99f806 --- /dev/null +++ b/titles/ongeki/brightmemory.py @@ -0,0 +1,58 @@ +from datetime import date, datetime, timedelta +from typing import Any, Dict +import pytz +import json + +from core.config import CoreConfig +from titles.ongeki.base import OngekiBase +from titles.ongeki.const import OngekiConstants +from titles.ongeki.config import OngekiConfig + +class OngekiBrightMemory(OngekiBase): + + def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None: + super().__init__(core_cfg, game_cfg) + self.version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY + + def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = super().handle_get_game_setting_api_request(data) + ret["gameSetting"]["dataVersion"] = "1.35.00" + ret["gameSetting"]["onlineDataVersion"] = "1.35.00" + ret["gameSetting"]["maxCountCharacter"] = 50 + ret["gameSetting"]["maxCountCard"] = 300 + ret["gameSetting"]["maxCountItem"] = 300 + ret["gameSetting"]["maxCountMusic"] = 50 + ret["gameSetting"]["maxCountMusicItem"] = 300 + ret["gameSetting"]["maxCountRivalMusic"] = 300 + return ret + + def handle_get_user_memory_chapter_api_request(self, data: Dict) -> Dict: + memories = self.data.item.get_memorychapters(data["userId"]) + if not memories: + return {"userId": data["userId"], "length":6, "userMemoryChapterList":[ + {"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70001, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0}, + {"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70002, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0}, + {"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70003, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0}, + {"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70004, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0}, + {"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70005, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0}, + {"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70099, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0} + ]} + + memory_chp = [] + for chp in memories: + tmp = chp._asdict() + tmp.pop("id") + tmp.pop("user") + memory_chp.append(tmp) + + return { + "userId": data["userId"], + "length": len(memory_chp), + "userMemoryChapterList": memory_chp + } + + def handle_get_game_music_release_state_api_request(self, data: Dict) -> Dict: + return { + "techScore": 0, + "cardNum": 0 + } \ No newline at end of file diff --git a/titles/ongeki/const.py b/titles/ongeki/const.py index da9614e..dd6c0f5 100644 --- a/titles/ongeki/const.py +++ b/titles/ongeki/const.py @@ -10,6 +10,7 @@ class OngekiConstants(): VER_ONGEKI_RED = 4 VER_ONGEKI_RED_PLUS = 5 VER_ONGEKI_BRIGHT = 6 + VER_ONGEKI_BRIGHT_MEMORY = 7 EVT_TYPES: Enum = Enum('EVT_TYPES', [ 'None', @@ -43,7 +44,7 @@ class OngekiConstants(): Lunatic = 10 VERSION_NAMES = ("ONGEKI", "ONGEKI+", "ONGEKI Summer", "ONGEKI Summer+", "ONGEKI Red", "ONGEKI Red+", - "ONGEKI Bright") + "ONGEKI Bright", "ONGEKI Bright Memory") @classmethod def game_ver_to_string(cls, ver: int): diff --git a/titles/ongeki/index.py b/titles/ongeki/index.py index d3b107e..c636659 100644 --- a/titles/ongeki/index.py +++ b/titles/ongeki/index.py @@ -17,6 +17,7 @@ from titles.ongeki.summerplus import OngekiSummerPlus from titles.ongeki.red import OngekiRed from titles.ongeki.redplus import OngekiRedPlus from titles.ongeki.bright import OngekiBright +from titles.ongeki.brightmemory import OngekiBrightMemory class OngekiServlet(): def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: @@ -32,6 +33,7 @@ class OngekiServlet(): OngekiRed(core_cfg, self.game_cfg), OngekiRedPlus(core_cfg, self.game_cfg), OngekiBright(core_cfg, self.game_cfg), + OngekiBrightMemory(core_cfg, self.game_cfg), ] self.logger = logging.getLogger("ongeki") @@ -69,8 +71,10 @@ class OngekiServlet(): internal_ver = OngekiConstants.VER_ONGEKI_RED elif version >= 125 and version < 130: # Red Plus internal_ver = OngekiConstants.VER_ONGEKI_RED_PLUS - elif version >= 130 and version < 135: # Red Plus + elif version >= 130 and version < 135: # Bright internal_ver = OngekiConstants.VER_ONGEKI_BRIGHT + elif version >= 135 and version < 140: # Bright Memory + internal_ver = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY 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 diff --git a/titles/ongeki/schema/item.py b/titles/ongeki/schema/item.py index cc09fda..c3f8c7e 100644 --- a/titles/ongeki/schema/item.py +++ b/titles/ongeki/schema/item.py @@ -107,6 +107,27 @@ chapter = Table( mysql_charset='utf8mb4' ) +memorychapter = Table( + "ongeki_user_memorychapter", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")), + Column("chapterId", Integer), + Column("gaugeId", Integer), + Column("gaugeNum", Integer), + Column("jewelCount", Integer), + Column("isStoryWatched", Boolean), + Column("isBossWatched", Boolean), + Column("isDialogWatched", Boolean), + Column("isEndingWatched", Boolean), + Column("isClear", Boolean), + Column("lastPlayMusicId", Integer), + Column("lastPlayMusicLevel", Integer), + Column("lastPlayMusicCategory", Integer), + UniqueConstraint("user", "chapterId", name="ongeki_user_memorychapter_uk"), + mysql_charset='utf8mb4' +) + item = Table( "ongeki_user_item", metadata, @@ -522,5 +543,24 @@ class OngekiItemData(BaseData): sql = select(boss).where(boss.c.user == aime_id) result = self.execute(sql) + if result is None: return None + return result.fetchall() + + def put_memorychapter(self, aime_id: int, memorychapter_data: Dict) -> Optional[int]: + memorychapter_data["user"] = aime_id + + sql = insert(memorychapter).values(**memorychapter_data) + conflict = sql.on_duplicate_key_update(**memorychapter_data) + result = self.execute(conflict) + + if result is None: + self.logger.warn(f"put_memorychapter: Failed to update! aime_id: {aime_id}") + return None + return result.lastrowid + + def get_memorychapters(self, aime_id: int) -> Optional[List[Dict]]: + sql = select(memorychapter).where(memorychapter.c.user == aime_id) + + result = self.execute(sql) if result is None: return None return result.fetchall() \ No newline at end of file diff --git a/titles/ongeki/schema/profile.py b/titles/ongeki/schema/profile.py index bdff67d..26c79b5 100644 --- a/titles/ongeki/schema/profile.py +++ b/titles/ongeki/schema/profile.py @@ -78,6 +78,7 @@ profile = Table( Column("overDamageBattlePoint", Integer, server_default="0"), Column("bestBattlePoint", Integer, server_default="0"), Column("lastEmoneyBrand", Integer, server_default="0"), + Column("isDialogWatchedSuggestMemory", Boolean), UniqueConstraint("user", "version", name="ongeki_profile_profile_uk"), mysql_charset='utf8mb4' ) diff --git a/titles/ongeki/schema/score.py b/titles/ongeki/schema/score.py index 08a6a86..e526005 100644 --- a/titles/ongeki/schema/score.py +++ b/titles/ongeki/schema/score.py @@ -28,6 +28,7 @@ score_best = Table( Column("isLock", Boolean, nullable=False), Column("clearStatus", Boolean, nullable=False), Column("isStoryWatched", Boolean, nullable=False), + Column("platinumScoreMax", Integer), UniqueConstraint("user", "musicId", "level", name="ongeki_best_score_uk"), mysql_charset='utf8mb4' ) @@ -96,6 +97,8 @@ playlog = Table( Column("isAllBreak", Boolean), Column("playerRating", Integer), Column("battlePoint", Integer), + Column("platinumScore", Integer), + Column("platinumScoreMax", Integer), mysql_charset='utf8mb4' ) From 4626ec36cd2426be2f1da71073a38bdc0416b544 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Fri, 3 Mar 2023 12:40:03 -0500 Subject: [PATCH 12/16] wacca: fix options not saving --- titles/wacca/base.py | 2 +- titles/wacca/schema/profile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/titles/wacca/base.py b/titles/wacca/base.py index 9df231e..25ec33b 100644 --- a/titles/wacca/base.py +++ b/titles/wacca/base.py @@ -746,7 +746,7 @@ class WaccaBase(): user_id = self.data.profile.profile_to_aime_user(req.profileId) for opt in req.optsUpdated: - self.data.profile.update_option(user_id, opt.id, opt.val) + self.data.profile.update_option(user_id, opt.opt_id, opt.opt_val) for update in req.datesUpdated: pass diff --git a/titles/wacca/schema/profile.py b/titles/wacca/schema/profile.py index 7237149..c2a15f6 100644 --- a/titles/wacca/schema/profile.py +++ b/titles/wacca/schema/profile.py @@ -211,7 +211,7 @@ class WaccaProfileData(BaseData): ) conflict = sql.on_duplicate_key_update( - value = sql.inserted.value + value = value ) result = self.execute(conflict) From 7953519e683b46e026fa0d362ad71081d575df7e Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Fri, 3 Mar 2023 13:03:48 -0500 Subject: [PATCH 13/16] wacca: fix UserInfoUpdateRequest, per #5 --- titles/wacca/handlers/helpers.py | 8 ++++++++ titles/wacca/handlers/user_info.py | 10 +++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/titles/wacca/handlers/helpers.py b/titles/wacca/handlers/helpers.py index 9f6ba8b..1192237 100644 --- a/titles/wacca/handlers/helpers.py +++ b/titles/wacca/handlers/helpers.py @@ -840,3 +840,11 @@ class GateTutorialFlag: 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] diff --git a/titles/wacca/handlers/user_info.py b/titles/wacca/handlers/user_info.py index 6498488..5665676 100644 --- a/titles/wacca/handlers/user_info.py +++ b/titles/wacca/handlers/user_info.py @@ -1,7 +1,7 @@ from typing import List, Dict from titles.wacca.handlers.base import BaseRequest, BaseResponse -from titles.wacca.handlers.helpers import UserOption +from titles.wacca.handlers.helpers import UserOption, DateUpdate # ---user/info/update--- class UserInfoUpdateRequest(BaseRequest): @@ -9,12 +9,16 @@ class UserInfoUpdateRequest(BaseRequest): super().__init__(data) self.profileId = int(self.params[0]) self.optsUpdated: List[UserOption] = [] - self.datesUpdated: List = self.params[3] + self.unknown2: List = self.params[2] + self.datesUpdated: List[DateUpdate] = [] self.favoritesAdded: List[int] = self.params[4] self.favoritesRemoved: List[int] = self.params[5] - for x in self.params[2]: + 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): From cd78ecd7ea6cd4b7449c657a52cc528396d4ec72 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Fri, 3 Mar 2023 13:07:10 -0500 Subject: [PATCH 14/16] wacca: fix typo in UserInfoUpdateRequest --- titles/wacca/handlers/user_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/wacca/handlers/user_info.py b/titles/wacca/handlers/user_info.py index 5665676..287b742 100644 --- a/titles/wacca/handlers/user_info.py +++ b/titles/wacca/handlers/user_info.py @@ -18,7 +18,7 @@ class UserInfoUpdateRequest(BaseRequest): self.optsUpdated.append(UserOption(x[0], x[1])) for x in self.params[3]: - self.datesUpdated.append(DateUpdate[x[0], x[1]]) + self.datesUpdated.append(DateUpdate([x[0], x[1]])) # ---user/info/getMyroom--- TODO: Understand this better class UserInfogetMyroomRequest(BaseRequest): From 2da12e515e35ef2abd7581d761a96c66d8b67306 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Fri, 3 Mar 2023 13:07:36 -0500 Subject: [PATCH 15/16] wacca: see previous --- titles/wacca/handlers/user_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/wacca/handlers/user_info.py b/titles/wacca/handlers/user_info.py index 287b742..d30da39 100644 --- a/titles/wacca/handlers/user_info.py +++ b/titles/wacca/handlers/user_info.py @@ -18,7 +18,7 @@ class UserInfoUpdateRequest(BaseRequest): self.optsUpdated.append(UserOption(x[0], x[1])) for x in self.params[3]: - self.datesUpdated.append(DateUpdate([x[0], x[1]])) + self.datesUpdated.append(DateUpdate(x[0], x[1])) # ---user/info/getMyroom--- TODO: Understand this better class UserInfogetMyroomRequest(BaseRequest): From 45fedd8425d6fc8e29e0bcc4f671a374d381c3bb Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Fri, 3 Mar 2023 13:12:06 -0500 Subject: [PATCH 16/16] wacca: tidy up UserStatusUpdateRequestV2 --- titles/wacca/handlers/user_status.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/titles/wacca/handlers/user_status.py b/titles/wacca/handlers/user_status.py index b852dd1..5874fec 100644 --- a/titles/wacca/handlers/user_status.py +++ b/titles/wacca/handlers/user_status.py @@ -275,11 +275,6 @@ class UserStatusUpdateRequestV1(BaseRequest): self.itemsRecieved.append(GenericItemRecv(itm[0], itm[1], itm[2])) class UserStatusUpdateRequestV2(UserStatusUpdateRequestV1): - isContinue = False - isFirstPlayFree = False - itemsUsed = [] - lastSongInfo: LastSongDetail - def __init__(self, data: Dict) -> None: super().__init__(data) self.isContinue = bool(data["params"][3])