forked from Hay1tsme/artemis
Initial Card Maker ONGEKI support
This commit is contained in:
parent
8fe0acae93
commit
3acc2dc197
1
core/data/schema/versions/SDDT_3_rollback.sql
Normal file
1
core/data/schema/versions/SDDT_3_rollback.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE ongeki_profile_data DROP COLUMN lastEmoneyCredit;
|
1
core/data/schema/versions/SDDT_4_upgrade.sql
Normal file
1
core/data/schema/versions/SDDT_4_upgrade.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE ongeki_profile_data ADD COLUMN lastEmoneyCredit INTEGER DEFAULT 0;
|
99
core/data/schema/versions/SDED_1_upgrade.sql
Normal file
99
core/data/schema/versions/SDED_1_upgrade.sql
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
CREATE TABLE ongeki_user_gacha (
|
||||||
|
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||||
|
user INT NOT NULL,
|
||||||
|
gachaId INT NOT NULL,
|
||||||
|
totalGachaCnt INT DEFAULT 0,
|
||||||
|
ceilingGachaCnt INT DEFAULT 0,
|
||||||
|
selectPoint INT DEFAULT 0,
|
||||||
|
useSelectPoint INT DEFAULT 0,
|
||||||
|
dailyGachaCnt INT DEFAULT 0,
|
||||||
|
fiveGachaCnt INT DEFAULT 0,
|
||||||
|
elevenGachaCnt INT DEFAULT 0,
|
||||||
|
dailyGachaDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT ongeki_user_gacha_uk UNIQUE (user, gachaId),
|
||||||
|
CONSTRAINT ongeki_user_gacha_ibfk_1 FOREIGN KEY (user) REFERENCES aime_user (id) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE ongeki_user_gacha_supply (
|
||||||
|
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||||
|
user INT NOT NULL,
|
||||||
|
cardId INT NOT NULL,
|
||||||
|
CONSTRAINT ongeki_user_gacha_supply_uk UNIQUE (user, cardId),
|
||||||
|
CONSTRAINT ongeki_user_gacha_supply_ibfk_1 FOREIGN KEY (user) REFERENCES aime_user (id) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE ongeki_static_gachas (
|
||||||
|
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||||
|
version INT NOT NULL,
|
||||||
|
gachaId INT NOT NULL,
|
||||||
|
gachaName VARCHAR(255) NOT NULL,
|
||||||
|
kind INT NOT NULL,
|
||||||
|
type INT DEFAULT 0,
|
||||||
|
isCeiling BOOLEAN DEFAULT 0,
|
||||||
|
maxSelectPoint INT DEFAULT 0,
|
||||||
|
ceilingCnt INT DEFAULT 10,
|
||||||
|
changeRateCnt1 INT DEFAULT 0,
|
||||||
|
changeRateCnt2 INT DEFAULT 0,
|
||||||
|
startDate TIMESTAMP DEFAULT '2018-01-01 00:00:00.0',
|
||||||
|
endDate TIMESTAMP DEFAULT '2018-01-01 00:00:00.0',
|
||||||
|
noticeStartDate TIMESTAMP DEFAULT '2018-01-01 00:00:00.0',
|
||||||
|
noticeEndDate TIMESTAMP DEFAULT '2038-01-01 00:00:00.0',
|
||||||
|
convertEndDate TIMESTAMP DEFAULT '2038-01-01 00:00:00.0',
|
||||||
|
CONSTRAINT ongeki_static_gachas_uk UNIQUE (version, gachaId, gachaName)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE ongeki_static_gacha_cards (
|
||||||
|
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
gachaId INT NOT NULL,
|
||||||
|
cardId INT NOT NULL,
|
||||||
|
rarity INT NOT NULL,
|
||||||
|
weight INT DEFAULT 1,
|
||||||
|
isPickup BOOLEAN DEFAULT 0,
|
||||||
|
isSelect BOOLEAN DEFAULT 1,
|
||||||
|
CONSTRAINT ongeki_static_gacha_cards_uk UNIQUE (gachaId, cardId)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE ongeki_static_cards (
|
||||||
|
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
version INT NOT NULL,
|
||||||
|
cardId INT NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
charaId INT NOT NULL,
|
||||||
|
nickName VARCHAR(255),
|
||||||
|
school VARCHAR(255) NOT NULL,
|
||||||
|
attribute VARCHAR(5) NOT NULL,
|
||||||
|
gakunen VARCHAR(255) NOT NULL,
|
||||||
|
rarity INT NOT NULL,
|
||||||
|
levelParam VARCHAR(255) NOT NULL,
|
||||||
|
skillId INT NOT NULL,
|
||||||
|
choKaikaSkillId INT NOT NULL,
|
||||||
|
cardNumber VARCHAR(255),
|
||||||
|
CONSTRAINT ongeki_static_cards_uk UNIQUE (version, cardId)
|
||||||
|
) CHARACTER SET utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE ongeki_user_print_detail (
|
||||||
|
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
user INT NOT NULL,
|
||||||
|
cardId INT NOT NULL,
|
||||||
|
cardType INT DEFAULT 0,
|
||||||
|
printDate TIMESTAMP NOT NULL,
|
||||||
|
serialId VARCHAR(20) NOT NULL,
|
||||||
|
placeId INT NOT NULL,
|
||||||
|
clientId VARCHAR(11) NOT NULL,
|
||||||
|
printerSerialId VARCHAR(20) NOT NULL,
|
||||||
|
isHolograph BOOLEAN DEFAULT 0,
|
||||||
|
isAutographed BOOLEAN DEFAULT 0,
|
||||||
|
printOption1 BOOLEAN DEFAULT 1,
|
||||||
|
printOption2 BOOLEAN DEFAULT 1,
|
||||||
|
printOption3 BOOLEAN DEFAULT 1,
|
||||||
|
printOption4 BOOLEAN DEFAULT 1,
|
||||||
|
printOption5 BOOLEAN DEFAULT 1,
|
||||||
|
printOption6 BOOLEAN DEFAULT 1,
|
||||||
|
printOption7 BOOLEAN DEFAULT 1,
|
||||||
|
printOption8 BOOLEAN DEFAULT 1,
|
||||||
|
printOption9 BOOLEAN DEFAULT 1,
|
||||||
|
printOption10 BOOLEAN DEFAULT 0,
|
||||||
|
FOREIGN KEY (user) REFERENCES aime_user(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT ongeki_user_print_detail_uk UNIQUE (serialId)
|
||||||
|
) CHARACTER SET utf8mb4;
|
3
example_config/cardmaker.yaml
Normal file
3
example_config/cardmaker.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
server:
|
||||||
|
enable: True
|
||||||
|
loglevel: "info"
|
@ -1,3 +1,26 @@
|
|||||||
server:
|
server:
|
||||||
enable: True
|
enable: True
|
||||||
loglevel: "info"
|
loglevel: "info"
|
||||||
|
|
||||||
|
gachas:
|
||||||
|
enabled_gachas:
|
||||||
|
- 1011
|
||||||
|
- 1012
|
||||||
|
- 1043
|
||||||
|
- 1067
|
||||||
|
- 1068
|
||||||
|
- 1069
|
||||||
|
- 1070
|
||||||
|
- 1071
|
||||||
|
- 1072
|
||||||
|
- 1073
|
||||||
|
- 1074
|
||||||
|
- 1075
|
||||||
|
- 1076
|
||||||
|
- 1077
|
||||||
|
- 1081
|
||||||
|
- 1085
|
||||||
|
- 1089
|
||||||
|
- 1104
|
||||||
|
- 1111
|
||||||
|
- 1135
|
@ -20,7 +20,10 @@ class ChuniBase():
|
|||||||
self.logger = logging.getLogger("chuni")
|
self.logger = logging.getLogger("chuni")
|
||||||
self.game = ChuniConstants.GAME_CODE
|
self.game = ChuniConstants.GAME_CODE
|
||||||
self.version = ChuniConstants.VER_CHUNITHM
|
self.version = ChuniConstants.VER_CHUNITHM
|
||||||
|
|
||||||
|
def handle_ping_request(self, data: Dict) -> Dict:
|
||||||
|
return {"returnCode": 1}
|
||||||
|
|
||||||
def handle_game_login_api_request(self, data: Dict) -> Dict:
|
def handle_game_login_api_request(self, data: Dict) -> Dict:
|
||||||
#self.data.base.log_event("chuni", "login", logging.INFO, {"version": self.version, "user": data["userId"]})
|
#self.data.base.log_event("chuni", "login", logging.INFO, {"version": self.version, "user": data["userId"]})
|
||||||
return { "returnCode": 1 }
|
return { "returnCode": 1 }
|
||||||
|
15
titles/cm/__init__.py
Normal file
15
titles/cm/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from titles.cm.index import CardMakerServlet
|
||||||
|
from titles.cm.const import CardMakerConstants
|
||||||
|
from titles.cm.read import CardMakerReader
|
||||||
|
|
||||||
|
index = CardMakerServlet
|
||||||
|
reader = CardMakerReader
|
||||||
|
|
||||||
|
use_default_title = True
|
||||||
|
include_protocol = True
|
||||||
|
title_secure = False
|
||||||
|
game_codes = [CardMakerConstants.GAME_CODE]
|
||||||
|
trailing_slash = True
|
||||||
|
use_default_host = True
|
||||||
|
|
||||||
|
current_schema_version = 1
|
84
titles/cm/base.py
Normal file
84
titles/cm/base.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
from datetime import date, datetime, timedelta
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from core.config import CoreConfig
|
||||||
|
from core.data.cache import cached
|
||||||
|
from titles.cm.const import CardMakerConstants
|
||||||
|
from titles.cm.config import CardMakerConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CardMakerBase():
|
||||||
|
def __init__(self, core_cfg: CoreConfig, game_cfg: CardMakerConfig) -> None:
|
||||||
|
self.core_cfg = core_cfg
|
||||||
|
self.game_cfg = game_cfg
|
||||||
|
self.date_time_format = "%Y-%m-%d %H:%M:%S"
|
||||||
|
self.date_time_format_ext = "%Y-%m-%d %H:%M:%S.%f" # needs to be lopped off at [:-5]
|
||||||
|
self.date_time_format_short = "%Y-%m-%d"
|
||||||
|
self.logger = logging.getLogger("cardmaker")
|
||||||
|
self.game = CardMakerConstants.GAME_CODE
|
||||||
|
self.version = CardMakerConstants.VER_CARD_MAKER
|
||||||
|
|
||||||
|
def handle_get_game_connect_api_request(self, data: Dict) -> Dict:
|
||||||
|
uri = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}"
|
||||||
|
|
||||||
|
# CHUNITHM = 0, maimai = 1, ONGEKI = 2
|
||||||
|
return {
|
||||||
|
"length": 3,
|
||||||
|
"gameConnectList": [
|
||||||
|
{
|
||||||
|
"modelKind": 0,
|
||||||
|
"type": 1,
|
||||||
|
"titleUri": f"{uri}/SDHD/200/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"modelKind": 1,
|
||||||
|
"type": 1,
|
||||||
|
"titleUri": f"{uri}/SDEZ/120/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"modelKind": 2,
|
||||||
|
"type": 1,
|
||||||
|
"titleUri": f"{uri}/SDDT/130/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
|
reboot_start = date.strftime(datetime.now() + timedelta(hours=3), self.date_time_format)
|
||||||
|
reboot_end = date.strftime(datetime.now() + timedelta(hours=4), self.date_time_format)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"gameSetting": {
|
||||||
|
"dataVersion": "1.30.00",
|
||||||
|
"ongekiCmVersion": "1.30.01",
|
||||||
|
"chuniCmVersion": "2.00.00",
|
||||||
|
"maimaiCmVersion": "1.20.00",
|
||||||
|
"requestInterval": 10,
|
||||||
|
"rebootStartTime": reboot_start,
|
||||||
|
"rebootEndTime": reboot_end,
|
||||||
|
"maxCountCharacter": 100,
|
||||||
|
"maxCountItem": 100,
|
||||||
|
"maxCountCard": 100,
|
||||||
|
"watermark": False,
|
||||||
|
"isMaintenance": False,
|
||||||
|
"isBackgroundDistribute": False
|
||||||
|
},
|
||||||
|
"isDumpUpload": False,
|
||||||
|
"isAou": False
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_get_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||||
|
return {
|
||||||
|
"placeId": data["placeId"],
|
||||||
|
"length": 0,
|
||||||
|
"clientBookkeepingList": []
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
||||||
|
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
||||||
|
|
||||||
|
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||||
|
return {"returnCode": 1, "apiName": "UpsertClientBookkeepingApi"}
|
1616
titles/cm/cm_data/MU3/static_cards.csv
Normal file
1616
titles/cm/cm_data/MU3/static_cards.csv
Normal file
File diff suppressed because it is too large
Load Diff
336
titles/cm/cm_data/MU3/static_gacha_cards.csv
Normal file
336
titles/cm/cm_data/MU3/static_gacha_cards.csv
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
"gachaId","cardId","rarity","weight","isPickup","isSelect"
|
||||||
|
1070,100984,4,1,0,1
|
||||||
|
1070,100997,3,2,0,1
|
||||||
|
1070,100998,3,2,0,1
|
||||||
|
1070,101020,2,3,0,1
|
||||||
|
1070,101021,2,3,0,1
|
||||||
|
1070,101022,2,3,0,1
|
||||||
|
1067,100982,4,1,0,0
|
||||||
|
1067,100983,4,1,0,0
|
||||||
|
1067,100996,3,2,0,0
|
||||||
|
1068,100075,2,3,0,0
|
||||||
|
1068,100182,2,3,0,0
|
||||||
|
1068,100348,2,3,0,0
|
||||||
|
1068,100232,2,3,0,0
|
||||||
|
1068,100417,2,3,0,0
|
||||||
|
1068,100755,2,3,0,0
|
||||||
|
1068,100077,3,2,0,0
|
||||||
|
1068,100271,3,2,0,0
|
||||||
|
1068,100425,3,2,0,0
|
||||||
|
1068,100758,3,2,0,0
|
||||||
|
1068,101000,3,2,0,0
|
||||||
|
1068,100284,4,1,0,0
|
||||||
|
1068,100767,4,1,0,0
|
||||||
|
1068,101293,4,1,0,0
|
||||||
|
1069,100069,2,3,0,0
|
||||||
|
1069,100183,2,3,0,0
|
||||||
|
1069,100349,2,3,0,0
|
||||||
|
1069,100233,2,3,0,0
|
||||||
|
1069,100416,2,3,0,0
|
||||||
|
1069,100071,3,2,0,0
|
||||||
|
1069,100272,3,2,0,0
|
||||||
|
1069,100427,3,2,0,0
|
||||||
|
1069,100805,3,2,0,0
|
||||||
|
1069,101300,3,2,0,0
|
||||||
|
1069,100285,4,1,0,0
|
||||||
|
1069,100768,4,1,0,0
|
||||||
|
1069,100988,4,1,0,0
|
||||||
|
1071,100275,4,1,0,0
|
||||||
|
1071,100437,4,1,0,0
|
||||||
|
1071,100780,4,1,0,0
|
||||||
|
1071,100006,3,2,0,0
|
||||||
|
1071,100007,3,2,0,0
|
||||||
|
1071,100249,3,2,0,0
|
||||||
|
1071,100262,3,2,0,0
|
||||||
|
1071,100418,3,2,0,0
|
||||||
|
1071,100003,2,3,0,0
|
||||||
|
1071,100004,2,3,0,0
|
||||||
|
1071,100173,2,3,0,0
|
||||||
|
1071,100223,2,3,0,0
|
||||||
|
1071,100339,2,3,0,0
|
||||||
|
1071,100692,2,3,0,0
|
||||||
|
1072,100017,4,1,0,0
|
||||||
|
1072,100276,4,1,0,0
|
||||||
|
1072,100760,4,1,0,0
|
||||||
|
1072,100015,3,2,0,0
|
||||||
|
1072,100016,3,2,0,0
|
||||||
|
1072,100250,3,2,0,0
|
||||||
|
1072,100263,3,2,0,0
|
||||||
|
1072,100423,3,2,0,0
|
||||||
|
1072,100765,3,2,0,0
|
||||||
|
1072,100012,2,3,0,0
|
||||||
|
1072,100013,2,3,0,0
|
||||||
|
1072,100174,2,3,0,0
|
||||||
|
1072,100224,2,3,0,0
|
||||||
|
1072,100340,2,3,0,0
|
||||||
|
1072,100693,2,3,0,0
|
||||||
|
1073,100026,4,1,0,0
|
||||||
|
1073,100277,4,1,0,0
|
||||||
|
1073,100761,4,1,0,0
|
||||||
|
1073,100024,3,2,0,0
|
||||||
|
1073,100025,3,2,0,0
|
||||||
|
1073,100251,3,2,0,0
|
||||||
|
1073,100264,3,2,0,0
|
||||||
|
1073,100430,3,2,0,0
|
||||||
|
1073,100021,2,3,0,0
|
||||||
|
1073,100022,2,3,0,0
|
||||||
|
1073,100175,2,3,0,0
|
||||||
|
1073,100225,2,3,0,0
|
||||||
|
1073,100341,2,3,0,0
|
||||||
|
1073,100694,2,3,0,0
|
||||||
|
1011,100454,4,1,0,0
|
||||||
|
1011,100980,4,1,0,0
|
||||||
|
1011,101553,4,1,0,0
|
||||||
|
1011,100253,3,1,0,0
|
||||||
|
1011,100241,3,1,0,0
|
||||||
|
1011,100240,3,1,0,0
|
||||||
|
1011,100239,3,1,0,0
|
||||||
|
1011,100238,3,1,0,0
|
||||||
|
1011,100237,3,1,0,0
|
||||||
|
1011,100236,3,1,0,0
|
||||||
|
1011,100261,3,1,0,0
|
||||||
|
1011,100246,3,1,0,0
|
||||||
|
1011,100245,3,1,0,0
|
||||||
|
1011,100242,3,1,0,0
|
||||||
|
1011,100243,3,1,0,0
|
||||||
|
1011,100254,3,1,0,0
|
||||||
|
1011,100338,3,1,0,0
|
||||||
|
1011,100337,3,1,0,0
|
||||||
|
1011,100336,3,1,0,0
|
||||||
|
1011,100248,3,1,0,0
|
||||||
|
1011,100247,3,1,0,0
|
||||||
|
1011,100244,3,1,0,0
|
||||||
|
1011,100259,3,1,0,0
|
||||||
|
1011,100257,3,1,0,0
|
||||||
|
1011,100258,3,1,0,0
|
||||||
|
1011,100636,3,1,0,0
|
||||||
|
1011,100634,3,1,0,0
|
||||||
|
1011,100255,3,1,0,0
|
||||||
|
1011,100256,3,1,0,0
|
||||||
|
1011,100252,3,1,0,0
|
||||||
|
1011,100638,3,1,0,0
|
||||||
|
1011,100639,3,1,0,0
|
||||||
|
1011,100637,3,1,0,0
|
||||||
|
1011,100772,3,1,0,0
|
||||||
|
1011,100667,3,1,0,0
|
||||||
|
1011,100666,3,1,0,0
|
||||||
|
1011,100665,3,1,0,0
|
||||||
|
1011,100643,3,1,0,0
|
||||||
|
1011,100640,3,1,0,0
|
||||||
|
1011,100641,3,1,0,0
|
||||||
|
1011,100642,3,1,0,0
|
||||||
|
1011,100688,3,1,0,0
|
||||||
|
1011,100645,3,1,0,0
|
||||||
|
1011,100646,3,1,0,0
|
||||||
|
1011,100644,3,1,0,0
|
||||||
|
1012,100644,3,1,0,0
|
||||||
|
1012,100646,3,1,0,0
|
||||||
|
1012,100645,3,1,0,0
|
||||||
|
1012,100688,3,1,0,0
|
||||||
|
1012,100642,3,1,0,0
|
||||||
|
1012,100641,3,1,0,0
|
||||||
|
1012,100640,3,1,0,0
|
||||||
|
1012,100643,3,1,0,0
|
||||||
|
1012,100665,3,1,0,0
|
||||||
|
1012,100666,3,1,0,0
|
||||||
|
1012,100667,3,1,0,0
|
||||||
|
1012,100634,3,1,0,0
|
||||||
|
1012,100636,3,1,0,0
|
||||||
|
1012,100772,3,1,0,0
|
||||||
|
1012,100638,3,1,0,0
|
||||||
|
1012,100637,3,1,0,0
|
||||||
|
1012,100639,3,1,0,0
|
||||||
|
1012,100252,3,1,0,0
|
||||||
|
1012,100256,3,1,0,0
|
||||||
|
1012,100255,3,1,0,0
|
||||||
|
1012,100258,3,1,0,0
|
||||||
|
1012,100257,3,1,0,0
|
||||||
|
1012,100259,3,1,0,0
|
||||||
|
1012,100244,3,1,0,0
|
||||||
|
1012,100247,3,1,0,0
|
||||||
|
1012,100248,3,1,0,0
|
||||||
|
1012,100336,3,1,0,0
|
||||||
|
1012,100337,3,1,0,0
|
||||||
|
1012,100338,3,1,0,0
|
||||||
|
1012,100254,3,1,0,0
|
||||||
|
1012,100243,3,1,0,0
|
||||||
|
1012,100242,3,1,0,0
|
||||||
|
1012,100245,3,1,0,0
|
||||||
|
1012,100246,3,1,0,0
|
||||||
|
1012,100261,3,1,0,0
|
||||||
|
1012,100236,3,1,0,0
|
||||||
|
1012,100237,3,1,0,0
|
||||||
|
1012,100238,3,1,0,0
|
||||||
|
1012,100239,3,1,0,0
|
||||||
|
1012,100240,3,1,0,0
|
||||||
|
1012,100241,3,1,0,0
|
||||||
|
1012,100253,3,1,0,0
|
||||||
|
1012,100454,4,1,0,0
|
||||||
|
1012,100980,4,1,0,0
|
||||||
|
1012,101553,4,1,0,0
|
||||||
|
1074,100985,4,1,0,0
|
||||||
|
1074,100999,3,1,0,0
|
||||||
|
1074,101000,3,1,0,0
|
||||||
|
1074,101023,2,1,0,0
|
||||||
|
1074,101024,2,1,0,0
|
||||||
|
1075,100060,4,1,0,0
|
||||||
|
1075,100434,4,1,0,0
|
||||||
|
1075,100059,3,1,0,0
|
||||||
|
1075,100268,3,1,0,0
|
||||||
|
1075,100420,3,1,0,0
|
||||||
|
1075,100763,3,1,0,0
|
||||||
|
1075,101003,3,1,0,0
|
||||||
|
1075,100057,2,1,0,0
|
||||||
|
1075,100179,2,1,0,0
|
||||||
|
1075,100229,2,1,0,0
|
||||||
|
1075,100345,2,1,0,0
|
||||||
|
1075,100415,2,1,0,0
|
||||||
|
1076,100054,4,1,0,0
|
||||||
|
1076,100282,4,1,0,0
|
||||||
|
1076,100726,4,1,0,0
|
||||||
|
1076,100053,3,1,0,0
|
||||||
|
1076,100269,3,1,0,0
|
||||||
|
1076,100422,3,1,0,0
|
||||||
|
1076,100757,3,1,0,0
|
||||||
|
1076,100051,2,1,0,0
|
||||||
|
1076,100180,2,1,0,0
|
||||||
|
1076,100230,2,1,0,0
|
||||||
|
1076,100346,2,1,0,0
|
||||||
|
1076,100414,2,1,0,0
|
||||||
|
1077,100984,4,1,0,1
|
||||||
|
1077,100997,3,1,0,1
|
||||||
|
1077,100998,3,1,0,1
|
||||||
|
1077,100986,4,1,0,1
|
||||||
|
1077,101001,3,1,0,1
|
||||||
|
1077,101002,3,1,0,1
|
||||||
|
1077,101025,2,1,0,1
|
||||||
|
1077,101026,2,1,0,1
|
||||||
|
1077,101027,2,1,0,1
|
||||||
|
1081,100987,4,1,0,0
|
||||||
|
1081,100988,4,1,0,0
|
||||||
|
1081,101003,3,1,0,0
|
||||||
|
1085,100008,4,1,0,1
|
||||||
|
1085,100017,4,1,0,1
|
||||||
|
1085,100026,4,1,0,1
|
||||||
|
1085,100034,4,1,0,1
|
||||||
|
1085,100041,4,1,0,1
|
||||||
|
1085,100048,4,1,0,1
|
||||||
|
1085,100054,4,1,0,1
|
||||||
|
1085,100060,4,1,0,1
|
||||||
|
1085,100066,4,1,0,1
|
||||||
|
1085,100078,4,1,0,1
|
||||||
|
1085,100072,4,1,0,1
|
||||||
|
1085,100084,4,1,0,1
|
||||||
|
1085,100090,4,1,0,1
|
||||||
|
1085,100282,4,1,0,1
|
||||||
|
1085,100285,4,1,0,1
|
||||||
|
1085,100284,4,1,0,1
|
||||||
|
1085,100286,4,1,0,1
|
||||||
|
1085,100280,4,1,0,1
|
||||||
|
1085,100276,4,1,0,1
|
||||||
|
1085,100277,4,1,0,1
|
||||||
|
1085,100275,4,1,0,1
|
||||||
|
1085,100278,4,1,0,1
|
||||||
|
1085,100431,4,1,0,1
|
||||||
|
1085,100407,4,1,0,1
|
||||||
|
1085,100432,4,1,0,1
|
||||||
|
1085,100433,4,1,0,1
|
||||||
|
1085,100434,4,1,0,1
|
||||||
|
1085,100435,4,1,0,1
|
||||||
|
1085,100436,4,1,0,1
|
||||||
|
1085,100437,4,1,0,1
|
||||||
|
1085,100438,4,1,0,1
|
||||||
|
1085,100439,4,1,0,1
|
||||||
|
1085,100760,4,1,0,1
|
||||||
|
1085,100761,4,1,0,1
|
||||||
|
1085,100779,4,1,0,1
|
||||||
|
1085,100767,4,1,0,1
|
||||||
|
1085,100780,4,1,0,1
|
||||||
|
1085,100784,4,1,0,1
|
||||||
|
1085,100768,4,1,0,1
|
||||||
|
1085,100725,4,1,0,1
|
||||||
|
1085,100726,4,1,0,1
|
||||||
|
1085,100984,4,1,0,1
|
||||||
|
1085,100985,4,1,0,1
|
||||||
|
1085,100987,4,1,0,1
|
||||||
|
1085,100988,4,1,0,1
|
||||||
|
1085,100986,4,1,0,1
|
||||||
|
1085,100989,4,1,0,1
|
||||||
|
1085,100982,4,1,0,1
|
||||||
|
1085,100983,4,1,0,1
|
||||||
|
1085,100787,4,1,0,1
|
||||||
|
1085,101293,4,1,0,1
|
||||||
|
1085,101294,4,1,0,1
|
||||||
|
1085,101295,4,1,0,1
|
||||||
|
1085,101296,4,1,0,1
|
||||||
|
1085,101297,4,1,0,1
|
||||||
|
1085,101320,4,1,0,1
|
||||||
|
1085,101567,4,1,0,1
|
||||||
|
1085,101592,4,1,0,1
|
||||||
|
1085,101593,4,1,0,1
|
||||||
|
1085,101594,4,1,0,1
|
||||||
|
1085,101595,4,1,0,1
|
||||||
|
1089,100989,4,1,0,0
|
||||||
|
1089,101004,3,1,0,0
|
||||||
|
1089,101005,3,1,0,0
|
||||||
|
1104,101293,4,1,0,0
|
||||||
|
1104,101294,4,1,0,0
|
||||||
|
1104,101298,3,1,0,0
|
||||||
|
1111,100008,4,1,0,1
|
||||||
|
1111,100017,4,1,0,1
|
||||||
|
1111,100026,4,1,0,1
|
||||||
|
1111,100034,4,1,0,1
|
||||||
|
1111,100041,4,1,0,1
|
||||||
|
1111,100048,4,1,0,1
|
||||||
|
1111,100054,4,1,0,1
|
||||||
|
1111,100060,4,1,0,1
|
||||||
|
1111,100066,4,1,0,1
|
||||||
|
1111,100078,4,1,0,1
|
||||||
|
1111,100072,4,1,0,1
|
||||||
|
1111,100084,4,1,0,1
|
||||||
|
1111,100090,4,1,0,1
|
||||||
|
1111,100282,4,1,0,1
|
||||||
|
1111,100285,4,1,0,1
|
||||||
|
1111,100284,4,1,0,1
|
||||||
|
1111,100286,4,1,0,1
|
||||||
|
1111,100280,4,1,0,1
|
||||||
|
1111,100276,4,1,0,1
|
||||||
|
1111,100277,4,1,0,1
|
||||||
|
1111,100275,4,1,0,1
|
||||||
|
1111,100278,4,1,0,1
|
||||||
|
1111,100431,4,1,0,1
|
||||||
|
1111,100407,4,1,0,1
|
||||||
|
1111,100432,4,1,0,1
|
||||||
|
1111,100433,4,1,0,1
|
||||||
|
1111,100434,4,1,1,1
|
||||||
|
1111,100435,4,1,1,1
|
||||||
|
1111,100436,4,1,0,1
|
||||||
|
1111,100437,4,1,0,1
|
||||||
|
1111,100438,4,1,0,1
|
||||||
|
1111,100439,4,1,0,1
|
||||||
|
1111,100760,4,1,1,1
|
||||||
|
1111,100761,4,1,0,1
|
||||||
|
1111,100779,4,1,0,1
|
||||||
|
1111,100767,4,1,0,1
|
||||||
|
1111,100780,4,1,1,1
|
||||||
|
1111,100784,4,1,1,1
|
||||||
|
1111,100768,4,1,0,1
|
||||||
|
1111,100725,4,1,1,1
|
||||||
|
1111,100726,4,1,1,1
|
||||||
|
1111,100985,4,1,1,1
|
||||||
|
1111,100988,4,1,1,1
|
||||||
|
1111,100989,4,1,1,1
|
||||||
|
1111,100982,4,1,1,1
|
||||||
|
1111,100983,4,1,1,1
|
||||||
|
1111,101293,4,1,1,1
|
||||||
|
1111,101294,4,1,1,1
|
||||||
|
1111,101295,4,1,1,1
|
||||||
|
1111,101320,4,1,1,1
|
||||||
|
1135,101567,4,1,0,0
|
||||||
|
1135,101592,4,1,0,0
|
||||||
|
1135,101594,4,1,0,0
|
||||||
|
1135,101595,4,1,0,0
|
||||||
|
1135,101566,3,1,0,0
|
||||||
|
1135,101602,3,1,0,0
|
||||||
|
1135,101603,3,1,0,0
|
||||||
|
1135,101619,2,1,0,0
|
|
104
titles/cm/cm_data/MU3/static_gachas.csv
Normal file
104
titles/cm/cm_data/MU3/static_gachas.csv
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
"version","gachaId","gachaName","type","kind","isCeiling","maxSelectPoint","ceilingCnt","changeRateCnt1","changeRateCnt2"
|
||||||
|
6,1011,"無料ガチャ",0,3,0,0,10,0,0
|
||||||
|
6,1012,"無料ガチャ(SR確定)",0,3,0,0,10,0,0
|
||||||
|
6,1043,"レギュラーガチャ",0,0,0,0,10,0,0
|
||||||
|
6,1067,"例えるなら大人のパッションフルーツ
|
||||||
|
リゾートプールガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1068,"柏木 咲姫
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1069,"井之原 小星
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1070,"目指すは優勝!
|
||||||
|
炎の体育祭リミテッドガチャ",0,1,1,110,10,0,0
|
||||||
|
6,1071,"星咲 あかり
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1072,"藤沢 柚子
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1073,"三角 葵
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1074,"おくれてきた
|
||||||
|
Halloweenガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1075,"早乙女 彩華
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1076,"桜井 春菜
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1077,"ふわふわすぺーす
|
||||||
|
お仕事体験リミテッドガチャ",0,1,1,110,10,0,0
|
||||||
|
6,1078,"高瀬 梨緒
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1079,"結城 莉玖
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1080,"藍原 椿
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1081,"今夜はおうちでパーティ☆
|
||||||
|
メリクリガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1082,"日向 千夏
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1083,"柏木 美亜
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1084,"東雲 つむぎ
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1085,"謹賀新年
|
||||||
|
福袋ガチャ",0,0,1,33,10,0,0
|
||||||
|
6,1086,"逢坂 茜
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1087,"珠洲島 有栖
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1088,"九條 楓
|
||||||
|
ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1089,"冬の魔法
|
||||||
|
スーパーウルトラウィンターガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1093,"高瀬 梨緒ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1094,"結城 莉玖ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1095,"藍原 椿ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1096,"早乙女 彩華ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1097,"桜井 春菜ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1098,"逢坂 茜ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1099,"九條 楓ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1100,"珠洲島 有栖ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1101,"LEAF属性オンリーガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1102,"AQUA属性オンリーガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1103,"FIRE属性オンリーガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1104,"夜明け前の双星ガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1105,"謎の洞窟 黄金は実在した!!ガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1106,"スウィートブライダルリミテッドガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1107,"忘れられない、愛(ピュア)とロックがここにある。ガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1108,"メルティ夜ふかしガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1109,"絵本の国のシューターズガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1110,"オンゲキ R.E.D. PLUS 大感謝祭ガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1111,"オンゲキ 3rd Anniversaryガチャ",0,1,1,33,10,0,0
|
||||||
|
6,1113,"柏木 咲姫ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1114,"井之原 小星ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1115,"星咲 あかりピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1116,"藤沢 柚子ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1117,"三角 葵ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1118,"日向 千夏ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1119,"柏木 美亜ピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1120,"東雲 つむぎピックアップガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1121,"LEAF属性オンリーガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1122,"FIRE属性オンリーガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1123,"AQUA属性オンリーガチャ",0,2,0,0,10,0,0
|
||||||
|
6,1125,"Let`s SHOOT!ガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1126,"ぽかぽか""温""ゲキ!いい湯だな リミテッドガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1127,"聖夜に煌めく イルミネーションガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1128,"bitter chocolate kiss ガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1134,"謹賀新年福袋ガチャ",0,1,0,0,10,0,0
|
||||||
|
6,1135,"オンゲキ bright 大感謝祭ガチャ",0,1,0,0,10,0,0
|
||||||
|
7,1140,"カラフルアンブレラガチャ",0,0,0,0,10,0,0
|
||||||
|
7,1141,"It's Showtime!ワンダフルサーカスガチャ",0,0,0,0,10,0,0
|
||||||
|
7,1147,"R.B.P. ピックアップガチャ",0,0,0,0,10,0,0
|
||||||
|
7,1148,"皇城 セツナ ピックアップガチャ",0,0,0,0,10,0,0
|
||||||
|
7,1149,"ASTERISM ピックアップガチャ",0,0,0,0,10,0,0
|
||||||
|
7,1153,"Memories of O.N.G.E.K.I.打ち上げガチャ",0,0,0,0,10,0,0
|
||||||
|
7,1156,"bright memory振り返りガチャ",0,0,0,0,10,0,0
|
||||||
|
7,1158,"レギュラーガチャ",0,0,0,100,0,0,0
|
||||||
|
7,1159,"オンゲキ&オンゲキ PLUS ピックアップガチャ",0,2,0,100,0,0,0
|
||||||
|
7,1160,"SUMMER & SUMMER PLUS ピックアップガチャ",0,2,0,100,0,0,0
|
||||||
|
7,1161,"R.E.D. & R.E.D. PLUS ピックアップガチャ",0,2,0,100,0,0,0
|
||||||
|
7,1162,"bright & bright MEMORY ピックアップガチャ",0,2,0,100,0,0,0
|
||||||
|
7,1163,"4周年記念!! 4rd Anniversaryセレクトガチャ",0,1,0,100,0,0,0
|
||||||
|
7,1164,"2023謹賀新年福袋ガチャ",0,1,0,100,0,0,0
|
||||||
|
7,1165,"5周年記念!! 5rd Anniversaryセレクトガチャ",0,1,0,100,0,0,0
|
||||||
|
7,1166,"2024謹賀新年福袋ガチャ",0,1,0,100,0,0,0
|
||||||
|
7,1167,"6周年記念!! 6rd Anniversaryセレクトガチャ",0,1,0,100,0,0,0
|
||||||
|
7,1168,"2025謹賀新年福袋ガチャ",0,1,0,100,0,0,0
|
|
19
titles/cm/config.py
Normal file
19
titles/cm/config.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from core.config import CoreConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CardMakerServerConfig():
|
||||||
|
def __init__(self, parent_config: "CardMakerConfig") -> None:
|
||||||
|
self.__config = parent_config
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enable(self) -> bool:
|
||||||
|
return CoreConfig.get_config_field(self.__config, 'cardmaker', 'server', 'enable', default=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def loglevel(self) -> int:
|
||||||
|
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'cardmaker', 'server', 'loglevel', default="info"))
|
||||||
|
|
||||||
|
|
||||||
|
class CardMakerConfig(dict):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.server = CardMakerServerConfig(self)
|
10
titles/cm/const.py
Normal file
10
titles/cm/const.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
class CardMakerConstants():
|
||||||
|
GAME_CODE = "SDED"
|
||||||
|
|
||||||
|
VER_CARD_MAKER = 0
|
||||||
|
|
||||||
|
VERSION_NAMES = ["Card Maker 1.34"]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def game_ver_to_string(cls, ver: int):
|
||||||
|
return cls.VERSION_NAMES[ver]
|
91
titles/cm/index.py
Normal file
91
titles/cm/index.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
from twisted.web.http import Request
|
||||||
|
import json
|
||||||
|
import inflection
|
||||||
|
import yaml
|
||||||
|
import string
|
||||||
|
import logging
|
||||||
|
import coloredlogs
|
||||||
|
import zlib
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
|
from core.config import CoreConfig
|
||||||
|
from titles.cm.config import CardMakerConfig
|
||||||
|
from titles.cm.const import CardMakerConstants
|
||||||
|
from titles.cm.base import CardMakerBase
|
||||||
|
|
||||||
|
|
||||||
|
class CardMakerServlet():
|
||||||
|
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||||
|
self.core_cfg = core_cfg
|
||||||
|
self.game_cfg = CardMakerConfig()
|
||||||
|
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/cardmaker.yaml")))
|
||||||
|
|
||||||
|
self.versions = [
|
||||||
|
CardMakerBase(core_cfg, self.game_cfg)
|
||||||
|
]
|
||||||
|
|
||||||
|
self.logger = logging.getLogger("cardmaker")
|
||||||
|
log_fmt_str = "[%(asctime)s] Card Maker | %(levelname)s | %(message)s"
|
||||||
|
log_fmt = logging.Formatter(log_fmt_str)
|
||||||
|
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cardmaker"), encoding='utf8',
|
||||||
|
when="d", backupCount=10)
|
||||||
|
|
||||||
|
fileHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
|
consoleHandler = logging.StreamHandler()
|
||||||
|
consoleHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
|
self.logger.addHandler(fileHandler)
|
||||||
|
self.logger.addHandler(consoleHandler)
|
||||||
|
|
||||||
|
self.logger.setLevel(self.game_cfg.server.loglevel)
|
||||||
|
coloredlogs.install(level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str)
|
||||||
|
|
||||||
|
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
|
||||||
|
req_raw = request.content.getvalue()
|
||||||
|
url_split = url_path.split("/")
|
||||||
|
internal_ver = 0
|
||||||
|
endpoint = url_split[len(url_split) - 1]
|
||||||
|
|
||||||
|
print(f"version: {version}")
|
||||||
|
|
||||||
|
if version >= 130 and version < 135: # Card Maker
|
||||||
|
internal_ver = CardMakerConstants.VER_CARD_MAKER
|
||||||
|
|
||||||
|
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
|
||||||
|
# doing encrypted. The likelyhood of false positives is low but
|
||||||
|
# technically not 0
|
||||||
|
self.logger.error("Encryption not supported at this time")
|
||||||
|
|
||||||
|
try:
|
||||||
|
unzip = zlib.decompress(req_raw)
|
||||||
|
|
||||||
|
except zlib.error as e:
|
||||||
|
self.logger.error(f"Failed to decompress v{version} {endpoint} request -> {e}")
|
||||||
|
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||||
|
|
||||||
|
req_data = json.loads(unzip)
|
||||||
|
|
||||||
|
self.logger.info(f"v{version} {endpoint} request - {req_data}")
|
||||||
|
|
||||||
|
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
||||||
|
|
||||||
|
try:
|
||||||
|
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||||
|
resp = handler(req_data)
|
||||||
|
|
||||||
|
except AttributeError as e:
|
||||||
|
self.logger.warning(f"Unhandled v{version} request {endpoint} - {e}")
|
||||||
|
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||||
|
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||||
|
|
||||||
|
if resp is None:
|
||||||
|
resp = {'returnCode': 1}
|
||||||
|
|
||||||
|
self.logger.info(f"Response {resp}")
|
||||||
|
|
||||||
|
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
199
titles/cm/read.py
Normal file
199
titles/cm/read.py
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
from decimal import Decimal
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import csv
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
from read import BaseReader
|
||||||
|
from core.config import CoreConfig
|
||||||
|
from titles.ongeki.database import OngekiData
|
||||||
|
from titles.cm.const import CardMakerConstants
|
||||||
|
from titles.ongeki.const import OngekiConstants
|
||||||
|
from titles.ongeki.config import OngekiConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CardMakerReader(BaseReader):
|
||||||
|
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str],
|
||||||
|
opt_dir: Optional[str], extra: Optional[str]) -> None:
|
||||||
|
super().__init__(config, version, bin_dir, opt_dir, extra)
|
||||||
|
self.ongeki_data = OngekiData(config)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.logger.info(
|
||||||
|
f"Start importer for {CardMakerConstants.game_ver_to_string(version)}")
|
||||||
|
except IndexError:
|
||||||
|
self.logger.error(f"Invalid ongeki version {version}")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
def read(self) -> None:
|
||||||
|
static_datas = {
|
||||||
|
"static_cards.csv": "read_ongeki_card_csv",
|
||||||
|
"static_gachas.csv": "read_ongeki_gacha_csv",
|
||||||
|
"static_gacha_cards.csv": "read_ongeki_gacha_card_csv"
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.bin_dir is not None:
|
||||||
|
for file, func in static_datas.items():
|
||||||
|
if os.path.exists(f"{self.bin_dir}/MU3/{file}"):
|
||||||
|
read_csv = getattr(CardMakerReader, func)
|
||||||
|
read_csv(self, f"{self.bin_dir}/MU3/{file}")
|
||||||
|
else:
|
||||||
|
self.logger.warn(f"Couldn't find {file} file in {self.bin_dir}, skipping")
|
||||||
|
|
||||||
|
if self.opt_dir is not None:
|
||||||
|
dir = self.get_data_directories(self.opt_dir)
|
||||||
|
|
||||||
|
# ONGEKI (MU3) cnnot easily access the bin data(A000.pac)
|
||||||
|
# so only opt_dir will work for now
|
||||||
|
self.read_gacha(f"{dir}/MU3/gacha")
|
||||||
|
self.read_card(f"{dir}/MU3/card")
|
||||||
|
|
||||||
|
def read_ongeki_card_csv(self, file_path: str) -> None:
|
||||||
|
self.logger.info(f"Reading cards from {file_path}...")
|
||||||
|
|
||||||
|
with open(file_path, encoding="utf-8") as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
for row in reader:
|
||||||
|
self.ongeki_data.static.put_card(
|
||||||
|
row["version"],
|
||||||
|
row["cardId"],
|
||||||
|
name=row["name"],
|
||||||
|
charaId=row["charaId"],
|
||||||
|
nickName=row["nickName"] if row["nickName"] != "" else None,
|
||||||
|
school=row["school"],
|
||||||
|
attribute=row["attribute"],
|
||||||
|
gakunen=row["gakunen"],
|
||||||
|
rarity=row["rarity"],
|
||||||
|
levelParam=row["levelParam"],
|
||||||
|
skillId=row["skillId"],
|
||||||
|
choKaikaSkillId=row["choKaikaSkillId"],
|
||||||
|
cardNumber=row["cardNumber"] if row["cardNumber"] != "" else None
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.info(f"Added card {row['cardId']}")
|
||||||
|
|
||||||
|
def read_ongeki_gacha_csv(self, file_path: str) -> None:
|
||||||
|
self.logger.info(f"Reading gachas from {file_path}...")
|
||||||
|
|
||||||
|
with open(file_path, encoding="utf-8") as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
for row in reader:
|
||||||
|
self.ongeki_data.static.put_gacha(
|
||||||
|
row["version"],
|
||||||
|
row["gachaId"],
|
||||||
|
row["gachaName"],
|
||||||
|
row["kind"],
|
||||||
|
type=row["type"],
|
||||||
|
isCeiling=True if row["isCeiling"] == "1" else False,
|
||||||
|
maxSelectPoint=row["maxSelectPoint"],
|
||||||
|
ceilingCnt=row["ceilingCnt"],
|
||||||
|
changeRateCnt1=row["changeRateCnt1"],
|
||||||
|
changeRateCnt2=row["changeRateCnt2"]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.info(f"Added gacha {row['gachaId']}")
|
||||||
|
|
||||||
|
def read_ongeki_gacha_card_csv(self, file_path: str) -> None:
|
||||||
|
self.logger.info(f"Reading gacha cards from {file_path}...")
|
||||||
|
|
||||||
|
with open(file_path, encoding="utf-8") as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
for row in reader:
|
||||||
|
self.ongeki_data.static.put_gacha_card(
|
||||||
|
row["gachaId"],
|
||||||
|
row["cardId"],
|
||||||
|
rarity=row["rarity"],
|
||||||
|
weight=row["weight"],
|
||||||
|
isPickup=True if row["isPickup"] == "1" else False,
|
||||||
|
isSelect=True if row["isSelect"] == "1" else False
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.info(f"Added card {row['cardId']} to gacha")
|
||||||
|
|
||||||
|
def read_ongeki_card(self, base_dir: str) -> None:
|
||||||
|
self.logger.info(f"Reading cards from {base_dir}...")
|
||||||
|
|
||||||
|
version_ids = {
|
||||||
|
'1000': OngekiConstants.VER_ONGEKI,
|
||||||
|
'1005': OngekiConstants.VER_ONGEKI_PLUS,
|
||||||
|
'1010': OngekiConstants.VER_ONGEKI_SUMMER,
|
||||||
|
'1015': OngekiConstants.VER_ONGEKI_SUMMER_PLUS,
|
||||||
|
'1020': OngekiConstants.VER_ONGEKI_RED,
|
||||||
|
'1025': OngekiConstants.VER_ONGEKI_RED_PLUS,
|
||||||
|
'1030': OngekiConstants.VER_ONGEKI_BRIGHT,
|
||||||
|
'1035': OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
||||||
|
}
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(base_dir):
|
||||||
|
for dir in dirs:
|
||||||
|
if os.path.exists(f"{root}/{dir}/Card.xml"):
|
||||||
|
with open(f"{root}/{dir}/Card.xml", "r", encoding="utf-8") as f:
|
||||||
|
troot = ET.fromstring(f.read())
|
||||||
|
|
||||||
|
card_id = int(troot.find('Name').find('id').text)
|
||||||
|
name = troot.find('Name').find('str').text
|
||||||
|
chara_id = int(troot.find('CharaID').find('id').text)
|
||||||
|
nick_name = troot.find('NickName').text
|
||||||
|
school = troot.find('School').find('str').text
|
||||||
|
attribute = troot.find('Attribute').text
|
||||||
|
gakunen = troot.find('Gakunen').find('str').text
|
||||||
|
rarity = OngekiConstants.RARITY_TYPES[
|
||||||
|
troot.find('Rarity').text].value
|
||||||
|
|
||||||
|
level_param = []
|
||||||
|
for lvl in troot.find('LevelParam').findall('int'):
|
||||||
|
level_param.append(lvl.text)
|
||||||
|
|
||||||
|
skill_id = int(troot.find('SkillID').find('id').text)
|
||||||
|
cho_kai_ka_skill_id = int(troot.find('ChoKaikaSkillID').find('id').text)
|
||||||
|
|
||||||
|
version = version_ids[
|
||||||
|
troot.find('VersionID').find('id').text]
|
||||||
|
card_number = troot.find('CardNumberString').text
|
||||||
|
|
||||||
|
self.ongeki_data.static.put_card(
|
||||||
|
version,
|
||||||
|
card_id,
|
||||||
|
name=name,
|
||||||
|
charaId=chara_id,
|
||||||
|
nickName=nick_name,
|
||||||
|
school=school,
|
||||||
|
attribute=attribute,
|
||||||
|
gakunen=gakunen,
|
||||||
|
rarity=rarity,
|
||||||
|
levelParam=','.join(level_param),
|
||||||
|
skillId=skill_id,
|
||||||
|
choKaikaSkillId=cho_kai_ka_skill_id,
|
||||||
|
cardNumber=card_number
|
||||||
|
)
|
||||||
|
self.logger.info(f"Added card {card_id}")
|
||||||
|
|
||||||
|
def read_ongeki_gacha(self, base_dir: str) -> None:
|
||||||
|
self.logger.info(f"Reading gachas from {base_dir}...")
|
||||||
|
|
||||||
|
# assuming some GachaKinds based on the GachaType
|
||||||
|
type_to_kind = {
|
||||||
|
"Normal": "Normal",
|
||||||
|
"Pickup": "Pickup",
|
||||||
|
"RecoverFiveShotFlag": "BonusRestored",
|
||||||
|
"Free": "Free",
|
||||||
|
"FreeSR": "Free"
|
||||||
|
}
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(base_dir):
|
||||||
|
for dir in dirs:
|
||||||
|
if os.path.exists(f"{root}/{dir}/Gacha.xml"):
|
||||||
|
with open(f"{root}/{dir}/Gacha.xml", "r", encoding="utf-8") as f:
|
||||||
|
troot = ET.fromstring(f.read())
|
||||||
|
|
||||||
|
name = troot.find('Name').find('str').text
|
||||||
|
id = int(troot.find('Name').find('id').text)
|
||||||
|
|
||||||
|
gacha_kind = OngekiConstants.CM_GACHA_KINDS[
|
||||||
|
type_to_kind[troot.find('Type').text]].value
|
||||||
|
|
||||||
|
self.ongeki_data.static.put_gacha(
|
||||||
|
self.version, id, name, gacha_kind)
|
||||||
|
self.logger.info(f"Added gacha {id}")
|
@ -16,6 +16,9 @@ class Mai2Base():
|
|||||||
self.data = Mai2Data(cfg)
|
self.data = Mai2Data(cfg)
|
||||||
self.logger = logging.getLogger("mai2")
|
self.logger = logging.getLogger("mai2")
|
||||||
|
|
||||||
|
def handle_ping_request(self, data: Dict) -> Dict:
|
||||||
|
return {"returnCode": 1}
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict):
|
def handle_get_game_setting_api_request(self, data: Dict):
|
||||||
reboot_start = date.strftime(datetime.now() + timedelta(hours=3), Mai2Constants.DATE_TIME_FORMAT)
|
reboot_start = date.strftime(datetime.now() + timedelta(hours=3), Mai2Constants.DATE_TIME_FORMAT)
|
||||||
reboot_end = date.strftime(datetime.now() + timedelta(hours=4), Mai2Constants.DATE_TIME_FORMAT)
|
reboot_end = date.strftime(datetime.now() + timedelta(hours=4), Mai2Constants.DATE_TIME_FORMAT)
|
||||||
@ -202,9 +205,9 @@ class Mai2Base():
|
|||||||
for fav in upsert["userFavoriteList"]:
|
for fav in upsert["userFavoriteList"]:
|
||||||
self.data.item.put_favorite(user_id, fav["kind"], fav["itemIdList"])
|
self.data.item.put_favorite(user_id, fav["kind"], fav["itemIdList"])
|
||||||
|
|
||||||
if "isNewFriendSeasonRankingList" in upsert and int(upsert["isNewFriendSeasonRankingList"]) > 0:
|
# if "isNewFriendSeasonRankingList" in upsert and int(upsert["isNewFriendSeasonRankingList"]) > 0:
|
||||||
for fsr in upsert["userFriendSeasonRankingList"]:
|
# for fsr in upsert["userFriendSeasonRankingList"]:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
def handle_user_logout_api_request(self, data: Dict) -> Dict:
|
def handle_user_logout_api_request(self, data: Dict) -> Dict:
|
||||||
pass
|
pass
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
from random import randint
|
||||||
import pytz
|
import pytz
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@ -8,8 +9,8 @@ from titles.ongeki.base import OngekiBase
|
|||||||
from titles.ongeki.const import OngekiConstants
|
from titles.ongeki.const import OngekiConstants
|
||||||
from titles.ongeki.config import OngekiConfig
|
from titles.ongeki.config import OngekiConfig
|
||||||
|
|
||||||
class OngekiBright(OngekiBase):
|
|
||||||
|
|
||||||
|
class OngekiBright(OngekiBase):
|
||||||
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = OngekiConstants.VER_ONGEKI_BRIGHT
|
self.version = OngekiConstants.VER_ONGEKI_BRIGHT
|
||||||
@ -19,3 +20,609 @@ class OngekiBright(OngekiBase):
|
|||||||
ret["gameSetting"]["dataVersion"] = "1.30.00"
|
ret["gameSetting"]["dataVersion"] = "1.30.00"
|
||||||
ret["gameSetting"]["onlineDataVersion"] = "1.30.00"
|
ret["gameSetting"]["onlineDataVersion"] = "1.30.00"
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||||
|
# first check for a bright memory profile after that check for a
|
||||||
|
# bright profile
|
||||||
|
p = (self.data.profile.get_profile_data(data["userId"], self.version+1)
|
||||||
|
or self.data.profile.get_profile_data(data["userId"], self.version))
|
||||||
|
if p is None:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
cards = self.data.card.get_user_cards(data["userId"])
|
||||||
|
if cards is None or len(cards) == 0:
|
||||||
|
# This should never happen
|
||||||
|
self.logger.error(
|
||||||
|
f"handle_get_user_data_api_request: Internal error - No cards found for user id {data['userId']}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# get the dict representation of the row so we can modify values
|
||||||
|
user_data = p._asdict()
|
||||||
|
|
||||||
|
# remove the values the game doesn't want
|
||||||
|
user_data.pop("id")
|
||||||
|
user_data.pop("user")
|
||||||
|
user_data.pop("version")
|
||||||
|
|
||||||
|
# TODO: replace datetime objects with strings
|
||||||
|
|
||||||
|
# add access code that we don't store
|
||||||
|
user_data["accessCode"] = cards[0]["access_code"]
|
||||||
|
|
||||||
|
# hardcode Card Maker version for now
|
||||||
|
user_data["compatibleCmVersion"] = "1.30.01"
|
||||||
|
|
||||||
|
return {"userId": data["userId"], "userData": user_data}
|
||||||
|
|
||||||
|
def handle_printer_login_api_request(self, data: Dict):
|
||||||
|
return {"returnCode": 1}
|
||||||
|
|
||||||
|
def handle_printer_logout_api_request(self, data: Dict):
|
||||||
|
return {"returnCode": 1}
|
||||||
|
|
||||||
|
def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict:
|
||||||
|
user_cards = self.data.item.get_cards(data["userId"])
|
||||||
|
if user_cards is None:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
max_ct = data["maxCount"]
|
||||||
|
next_idx = data["nextIndex"]
|
||||||
|
start_idx = next_idx
|
||||||
|
end_idx = max_ct + start_idx
|
||||||
|
|
||||||
|
if len(user_cards[start_idx:]) > max_ct:
|
||||||
|
next_idx += max_ct
|
||||||
|
else:
|
||||||
|
next_idx = -1
|
||||||
|
|
||||||
|
card_list = []
|
||||||
|
for card in user_cards:
|
||||||
|
tmp = card._asdict()
|
||||||
|
tmp.pop("id")
|
||||||
|
tmp.pop("user")
|
||||||
|
card_list.append(tmp)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"userId": data["userId"],
|
||||||
|
"length": len(card_list[start_idx:end_idx]),
|
||||||
|
"nextIndex": next_idx,
|
||||||
|
"userCardList": card_list[start_idx:end_idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
|
||||||
|
user_characters = self.data.item.get_characters(data["userId"])
|
||||||
|
if user_characters is None:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
max_ct = data["maxCount"]
|
||||||
|
next_idx = data["nextIndex"]
|
||||||
|
start_idx = next_idx
|
||||||
|
end_idx = max_ct + start_idx
|
||||||
|
|
||||||
|
if len(user_characters[start_idx:]) > max_ct:
|
||||||
|
next_idx += max_ct
|
||||||
|
else:
|
||||||
|
next_idx = -1
|
||||||
|
|
||||||
|
character_list = []
|
||||||
|
for character in user_characters:
|
||||||
|
tmp = character._asdict()
|
||||||
|
tmp.pop("id")
|
||||||
|
tmp.pop("user")
|
||||||
|
character_list.append(tmp)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"userId": data["userId"],
|
||||||
|
"length": len(character_list[start_idx:end_idx]),
|
||||||
|
"nextIndex": next_idx,
|
||||||
|
"userCharacterList": character_list[start_idx:end_idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_get_user_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
|
user_gachas = self.data.item.get_user_gachas(data["userId"])
|
||||||
|
if user_gachas is None:
|
||||||
|
return {
|
||||||
|
"userId": data["userId"],
|
||||||
|
"length": 0,
|
||||||
|
"userGachaList": []
|
||||||
|
}
|
||||||
|
|
||||||
|
user_gacha_list = []
|
||||||
|
for gacha in user_gachas:
|
||||||
|
tmp = gacha._asdict()
|
||||||
|
tmp.pop("id")
|
||||||
|
tmp.pop("user")
|
||||||
|
tmp["dailyGachaDate"] = datetime.strftime(
|
||||||
|
tmp["dailyGachaDate"], "%Y-%m-%d")
|
||||||
|
user_gacha_list.append(tmp)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"userId": data["userId"],
|
||||||
|
"length": len(user_gacha_list),
|
||||||
|
"userGachaList": user_gacha_list
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||||
|
return self.handle_get_user_item_api_request(data)
|
||||||
|
|
||||||
|
def handle_cm_get_user_gacha_supply_api_request(self, data: Dict) -> Dict:
|
||||||
|
# not used for now? not sure what it even does
|
||||||
|
user_gacha_supplies = self.data.item.get_user_gacha_supplies(
|
||||||
|
data["userId"])
|
||||||
|
if user_gacha_supplies is None:
|
||||||
|
return {
|
||||||
|
"supplyId": 1,
|
||||||
|
"length": 0,
|
||||||
|
"supplyCardList": []
|
||||||
|
}
|
||||||
|
|
||||||
|
supply_list = [gacha["cardId"] for gacha in user_gacha_supplies]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"supplyId": 1,
|
||||||
|
"length": len(supply_list),
|
||||||
|
"supplyCardList": supply_list
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_get_game_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
|
"""
|
||||||
|
returns all current active banners (gachas)
|
||||||
|
"Select Gacha" requires maxSelectPoint set and isCeiling set to 1
|
||||||
|
"""
|
||||||
|
game_gachas = []
|
||||||
|
# for every gacha_id in the OngekiConfig, grab the banner from the db
|
||||||
|
for gacha_id in self.game_cfg.gachas.enabled_gachas:
|
||||||
|
game_gacha = self.data.static.get_gacha(self.version, gacha_id)
|
||||||
|
if game_gacha:
|
||||||
|
game_gachas.append(game_gacha)
|
||||||
|
|
||||||
|
# clean the database rows
|
||||||
|
game_gacha_list = []
|
||||||
|
for gacha in game_gachas:
|
||||||
|
tmp = gacha._asdict()
|
||||||
|
tmp.pop("id")
|
||||||
|
tmp.pop("version")
|
||||||
|
tmp["startDate"] = datetime.strftime(
|
||||||
|
tmp["startDate"], "%Y-%m-%d %H:%M:%S")
|
||||||
|
tmp["endDate"] = datetime.strftime(
|
||||||
|
tmp["endDate"], "%Y-%m-%d %H:%M:%S")
|
||||||
|
tmp["noticeStartDate"] = datetime.strftime(
|
||||||
|
tmp["noticeStartDate"], "%Y-%m-%d %H:%M:%S")
|
||||||
|
tmp["noticeEndDate"] = datetime.strftime(
|
||||||
|
tmp["noticeEndDate"], "%Y-%m-%d %H:%M:%S")
|
||||||
|
tmp["convertEndDate"] = datetime.strftime(
|
||||||
|
tmp["convertEndDate"], "%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
# make sure to only show gachas for the current version
|
||||||
|
# so only up to bright, 1140 is the first bright memory gacha
|
||||||
|
if tmp["gachaId"] < 1140:
|
||||||
|
game_gacha_list.append(tmp)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"length": len(game_gacha_list),
|
||||||
|
"gameGachaList": game_gacha_list,
|
||||||
|
# no clue
|
||||||
|
"registIdList": []
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_roll_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
|
"""
|
||||||
|
Handle a gacha roll API request
|
||||||
|
"""
|
||||||
|
gacha_id = data["gachaId"]
|
||||||
|
num_rolls = data["times"]
|
||||||
|
# change_rate is the 5 gacha rool SR gurantee once a week
|
||||||
|
change_rate = data["changeRate"]
|
||||||
|
# SSR book which guarantees a SSR card, itemKind=15, itemId=1
|
||||||
|
book_used = data["bookUseCount"]
|
||||||
|
if num_rolls not in {1, 5, 11}:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# https://gamerch.com/ongeki/entry/462978
|
||||||
|
|
||||||
|
# 77% chance of gett ing a R card
|
||||||
|
# 20% chance of getting a SR card
|
||||||
|
# 3% chance of getting a SSR card
|
||||||
|
rarity = [1 for _ in range(77)]
|
||||||
|
rarity += [2 for _ in range(20)]
|
||||||
|
rarity += [3 for _ in range(3)]
|
||||||
|
|
||||||
|
# gachaId 1011 is "無料ガチャ" (free gacha), which requires GatchaTickets
|
||||||
|
# itemKind=11, itemId=1 and appearenty sucks
|
||||||
|
# 94% chance of getting a R card
|
||||||
|
# 5% chance of getting a SR card
|
||||||
|
# 1% chance of getting a SSR card
|
||||||
|
if gacha_id == 1011:
|
||||||
|
rarity = [1 for _ in range(94)]
|
||||||
|
rarity += [2 for _ in range(5)]
|
||||||
|
rarity += [3 for _ in range(1)]
|
||||||
|
|
||||||
|
# gachaId 1012 is "無料ガチャ(SR確定)" (SR confirmed! free gacha), which
|
||||||
|
# requires GatchaTickets itemKind=11, itemId=4 and always guarantees
|
||||||
|
# a SR card or higher
|
||||||
|
# 92% chance of getting a SR card
|
||||||
|
# 8% chance of getting a SSR card
|
||||||
|
elif gacha_id == 1012:
|
||||||
|
rarity = [2 for _ in range(92)]
|
||||||
|
rarity += [3 for _ in range(8)]
|
||||||
|
|
||||||
|
assert len(rarity) == 100
|
||||||
|
|
||||||
|
# uniform distribution to get the rarity of the card
|
||||||
|
rolls = [rarity[randint(0, len(rarity)-1)] for _ in range(num_rolls)]
|
||||||
|
|
||||||
|
# if SSR book used, make sure you always get one SSR
|
||||||
|
if book_used == 1:
|
||||||
|
if rolls.count(3) == 0:
|
||||||
|
# if there is no SSR, re-roll
|
||||||
|
return self.handle_roll_gacha_api_request(data)
|
||||||
|
# make sure that 11 rolls always have at least 1 SR or SSR
|
||||||
|
elif (num_rolls == 5 and change_rate is True) or num_rolls == 11:
|
||||||
|
if rolls.count(2) == 0 and rolls.count(3) == 0:
|
||||||
|
# if there is no SR or SSR, re-roll
|
||||||
|
return self.handle_roll_gacha_api_request(data)
|
||||||
|
|
||||||
|
# get a list of cards for each rarity
|
||||||
|
cards_r = self.data.static.get_cards_by_rarity(self.version, 1)
|
||||||
|
cards_sr, cards_ssr = [], []
|
||||||
|
|
||||||
|
# free gachas are only allowed to get their specific cards! (R irrelevant)
|
||||||
|
if gacha_id in {1011, 1012}:
|
||||||
|
gacha_cards = self.data.static.get_gacha_cards(gacha_id)
|
||||||
|
for card in gacha_cards:
|
||||||
|
if card["rarity"] == 3:
|
||||||
|
cards_sr.append({
|
||||||
|
"cardId": card["cardId"],
|
||||||
|
"rarity": 2
|
||||||
|
})
|
||||||
|
elif card["rarity"] == 4:
|
||||||
|
cards_ssr.append({
|
||||||
|
"cardId": card["cardId"],
|
||||||
|
"rarity": 3
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
cards_sr = self.data.static.get_cards_by_rarity(self.version, 2)
|
||||||
|
cards_ssr = self.data.static.get_cards_by_rarity(self.version, 3)
|
||||||
|
|
||||||
|
# get the promoted cards for that gacha and add them multiple
|
||||||
|
# times to increase chances by factor chances
|
||||||
|
chances = 10
|
||||||
|
|
||||||
|
gacha_cards = self.data.static.get_gacha_cards(gacha_id)
|
||||||
|
for card in gacha_cards:
|
||||||
|
# make sure to add the cards to the corresponding rarity
|
||||||
|
if card["rarity"] == 2:
|
||||||
|
cards_r += [{
|
||||||
|
"cardId": card["cardId"],
|
||||||
|
"rarity": 1
|
||||||
|
}] * chances
|
||||||
|
if card["rarity"] == 3:
|
||||||
|
cards_sr += [{
|
||||||
|
"cardId": card["cardId"],
|
||||||
|
"rarity": 2
|
||||||
|
}] * chances
|
||||||
|
elif card["rarity"] == 4:
|
||||||
|
cards_ssr += [{
|
||||||
|
"cardId": card["cardId"],
|
||||||
|
"rarity": 3
|
||||||
|
}] * chances
|
||||||
|
|
||||||
|
# get the card id for each roll
|
||||||
|
rolled_cards = []
|
||||||
|
for i in range(len(rolls)):
|
||||||
|
if rolls[i] == 1:
|
||||||
|
rolled_cards.append(cards_r[randint(0, len(cards_r)-1)])
|
||||||
|
elif rolls[i] == 2:
|
||||||
|
rolled_cards.append(cards_sr[randint(0, len(cards_sr)-1)])
|
||||||
|
elif rolls[i] == 3:
|
||||||
|
rolled_cards.append(cards_ssr[randint(0, len(cards_ssr)-1)])
|
||||||
|
|
||||||
|
game_gacha_card_list = []
|
||||||
|
for card in rolled_cards:
|
||||||
|
game_gacha_card_list.append({
|
||||||
|
"gachaId": data["gachaId"],
|
||||||
|
"cardId": card["cardId"],
|
||||||
|
# +1 because Card Maker is weird
|
||||||
|
"rarity": card["rarity"] + 1,
|
||||||
|
"weight": 1,
|
||||||
|
"isPickup": False,
|
||||||
|
"isSelect": False
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"length": len(game_gacha_card_list),
|
||||||
|
"gameGachaCardList": game_gacha_card_list
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_cm_upsert_user_gacha_api_request(self, data: Dict):
|
||||||
|
upsert = data["cmUpsertUserGacha"]
|
||||||
|
user_id = data["userId"]
|
||||||
|
|
||||||
|
gacha_id = data["gachaId"]
|
||||||
|
gacha_count = data["gachaCnt"]
|
||||||
|
play_date = datetime.strptime(data["playDate"][:10], '%Y-%m-%d')
|
||||||
|
select_point = data["selectPoint"]
|
||||||
|
|
||||||
|
total_gacha_count, ceiling_gacha_count = 0, 0
|
||||||
|
daily_gacha_cnt, five_gacha_cnt, eleven_gacha_cnt = 0, 0, 0
|
||||||
|
daily_gacha_date = datetime.strptime('2000-01-01', '%Y-%m-%d')
|
||||||
|
|
||||||
|
# check if the user previously rolled the exact same gacha
|
||||||
|
user_gacha = self.data.item.get_user_gacha(user_id, gacha_id)
|
||||||
|
if user_gacha:
|
||||||
|
total_gacha_count = user_gacha["totalGachaCnt"]
|
||||||
|
ceiling_gacha_count = user_gacha["ceilingGachaCnt"]
|
||||||
|
daily_gacha_cnt = user_gacha["dailyGachaCnt"]
|
||||||
|
five_gacha_cnt = user_gacha["fiveGachaCnt"]
|
||||||
|
eleven_gacha_cnt = user_gacha["elevenGachaCnt"]
|
||||||
|
# parse just the year, month and date
|
||||||
|
daily_gacha_date = user_gacha["dailyGachaDate"]
|
||||||
|
|
||||||
|
# if the saved dailyGachaDate is different from the roll,
|
||||||
|
# reset dailyGachaCnt and change the date
|
||||||
|
if daily_gacha_date != play_date:
|
||||||
|
daily_gacha_date = play_date
|
||||||
|
daily_gacha_cnt = 0
|
||||||
|
|
||||||
|
self.data.item.put_user_gacha(
|
||||||
|
user_id,
|
||||||
|
gacha_id,
|
||||||
|
totalGachaCnt=total_gacha_count + gacha_count,
|
||||||
|
ceilingGachaCnt=ceiling_gacha_count + gacha_count,
|
||||||
|
selectPoint=select_point,
|
||||||
|
useSelectPoint=0,
|
||||||
|
dailyGachaCnt=daily_gacha_cnt + gacha_count,
|
||||||
|
fiveGachaCnt=five_gacha_cnt+1 if gacha_count == 5 else five_gacha_cnt,
|
||||||
|
elevenGachaCnt=eleven_gacha_cnt+1 if gacha_count == 11 else eleven_gacha_cnt,
|
||||||
|
dailyGachaDate=daily_gacha_date
|
||||||
|
)
|
||||||
|
|
||||||
|
if "userData" in upsert and len(upsert["userData"]) > 0:
|
||||||
|
# check if the profile is a bright memory profile
|
||||||
|
p = self.data.profile.get_profile_data(data["userId"], self.version+1)
|
||||||
|
if p is not None:
|
||||||
|
# save the bright memory profile
|
||||||
|
self.data.profile.put_profile_data(
|
||||||
|
user_id, self.version+1, upsert["userData"][0])
|
||||||
|
else:
|
||||||
|
# save the bright profile
|
||||||
|
self.data.profile.put_profile_data(
|
||||||
|
user_id, self.version, upsert["userData"][0])
|
||||||
|
|
||||||
|
if "userCharacterList" in upsert:
|
||||||
|
for x in upsert["userCharacterList"]:
|
||||||
|
self.data.item.put_character(user_id, x)
|
||||||
|
|
||||||
|
if "userItemList" in upsert:
|
||||||
|
for x in upsert["userItemList"]:
|
||||||
|
self.data.item.put_item(user_id, x)
|
||||||
|
|
||||||
|
if "userCardList" in upsert:
|
||||||
|
for x in upsert["userCardList"]:
|
||||||
|
self.data.item.put_card(user_id, x)
|
||||||
|
|
||||||
|
# TODO?
|
||||||
|
# if "gameGachaCardList" in upsert:
|
||||||
|
# for x in upsert["gameGachaCardList"]:
|
||||||
|
|
||||||
|
return {'returnCode': 1, 'apiName': 'CMUpsertUserGachaApi'}
|
||||||
|
|
||||||
|
def handle_cm_upsert_user_select_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
|
upsert = data["cmUpsertUserSelectGacha"]
|
||||||
|
user_id = data["userId"]
|
||||||
|
|
||||||
|
if "userData" in upsert and len(upsert["userData"]) > 0:
|
||||||
|
# check if the profile is a bright memory profile
|
||||||
|
p = self.data.profile.get_profile_data(data["userId"], self.version+1)
|
||||||
|
if p is not None:
|
||||||
|
# save the bright memory profile
|
||||||
|
self.data.profile.put_profile_data(
|
||||||
|
user_id, self.version+1, upsert["userData"][0])
|
||||||
|
else:
|
||||||
|
# save the bright profile
|
||||||
|
self.data.profile.put_profile_data(
|
||||||
|
user_id, self.version, upsert["userData"][0])
|
||||||
|
|
||||||
|
if "userCharacterList" in upsert:
|
||||||
|
for x in upsert["userCharacterList"]:
|
||||||
|
self.data.item.put_character(user_id, x)
|
||||||
|
|
||||||
|
if "userCardList" in upsert:
|
||||||
|
for x in upsert["userCardList"]:
|
||||||
|
self.data.item.put_card(user_id, x)
|
||||||
|
|
||||||
|
if "selectGachaLogList" in data:
|
||||||
|
for x in data["selectGachaLogList"]:
|
||||||
|
self.data.item.put_user_gacha(
|
||||||
|
user_id,
|
||||||
|
x["gachaId"],
|
||||||
|
selectPoint=0,
|
||||||
|
useSelectPoint=x["useSelectPoint"]
|
||||||
|
)
|
||||||
|
|
||||||
|
return {'returnCode': 1, 'apiName': 'cmUpsertUserSelectGacha'}
|
||||||
|
|
||||||
|
def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict:
|
||||||
|
game_gacha_cards = self.data.static.get_gacha_cards(data["gachaId"])
|
||||||
|
if game_gacha_cards == []:
|
||||||
|
# fallback to be at least able to select that gacha
|
||||||
|
return {
|
||||||
|
"gachaId": data["gachaId"],
|
||||||
|
"length": 6,
|
||||||
|
"isPickup": False,
|
||||||
|
"gameGachaCardList": [
|
||||||
|
{
|
||||||
|
"gachaId": data["gachaId"],
|
||||||
|
"cardId": 100984,
|
||||||
|
"rarity": 4,
|
||||||
|
"weight": 1,
|
||||||
|
"isPickup": False,
|
||||||
|
"isSelect": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gachaId": data["gachaId"],
|
||||||
|
"cardId": 100997,
|
||||||
|
"rarity": 3,
|
||||||
|
"weight": 2,
|
||||||
|
"isPickup": False,
|
||||||
|
"isSelect": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gachaId": data["gachaId"],
|
||||||
|
"cardId": 100998,
|
||||||
|
"rarity": 3,
|
||||||
|
"weight": 2,
|
||||||
|
"isPickup": False,
|
||||||
|
"isSelect": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gachaId": data["gachaId"],
|
||||||
|
"cardId": 101020,
|
||||||
|
"rarity": 2,
|
||||||
|
"weight": 3,
|
||||||
|
"isPickup": False,
|
||||||
|
"isSelect": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gachaId": data["gachaId"],
|
||||||
|
"cardId": 101021,
|
||||||
|
"rarity": 2,
|
||||||
|
"weight": 3,
|
||||||
|
"isPickup": False,
|
||||||
|
"isSelect": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gachaId": data["gachaId"],
|
||||||
|
"cardId": 101022,
|
||||||
|
"rarity": 2,
|
||||||
|
"weight": 3,
|
||||||
|
"isPickup": False,
|
||||||
|
"isSelect": True
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"emissionList": [],
|
||||||
|
"afterCalcList": [],
|
||||||
|
"ssrBookCalcList": []
|
||||||
|
}
|
||||||
|
|
||||||
|
game_gacha_card_list = []
|
||||||
|
for gacha_card in game_gacha_cards:
|
||||||
|
tmp = gacha_card._asdict()
|
||||||
|
tmp.pop("id")
|
||||||
|
game_gacha_card_list.append(tmp)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"gachaId": data["gachaId"],
|
||||||
|
"length": len(game_gacha_card_list),
|
||||||
|
"isPickup": False,
|
||||||
|
"gameGachaCardList": game_gacha_card_list,
|
||||||
|
# again no clue
|
||||||
|
"emissionList": [],
|
||||||
|
"afterCalcList": [],
|
||||||
|
"ssrBookCalcList": []
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_get_game_theater_api_request(self, data: Dict) -> Dict:
|
||||||
|
"""
|
||||||
|
shows a banner after every print, not sure what its used for
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"length": 1,
|
||||||
|
"gameTheaterList": [{
|
||||||
|
"theaterId": 1,
|
||||||
|
"theaterName": "theaterName",
|
||||||
|
"startDate": "2018-01-01 00:00:00.0",
|
||||||
|
"endDate": "2038-01-01 00:00:00.0",
|
||||||
|
"gameSubTheaterList": [{
|
||||||
|
"theaterId": 1,
|
||||||
|
"id": 2,
|
||||||
|
"no": 4
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"registIdList": []
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
return {
|
||||||
|
"length": 0,
|
||||||
|
"gameTheaterList": [],
|
||||||
|
"registIdList": []
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_cm_upsert_user_print_playlog_api_request(self, data: Dict) -> Dict:
|
||||||
|
return {
|
||||||
|
"returnCode": 1,
|
||||||
|
"orderId": 0,
|
||||||
|
"serialId": "11111111111111111111",
|
||||||
|
"apiName": "CMUpsertUserPrintPlaylogApi"
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
|
||||||
|
return {
|
||||||
|
"returnCode": 1,
|
||||||
|
"orderId": 0,
|
||||||
|
"serialId": "11111111111111111111",
|
||||||
|
"apiName": "CMUpsertUserPrintlogApi"
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
|
||||||
|
user_print_detail = data["userPrintDetail"]
|
||||||
|
|
||||||
|
# generate random serial id
|
||||||
|
serial_id = ''.join([str(randint(0, 9)) for _ in range(20)])
|
||||||
|
|
||||||
|
# not needed because are either zero or unset
|
||||||
|
user_print_detail.pop("orderId")
|
||||||
|
user_print_detail.pop("printNumber")
|
||||||
|
user_print_detail.pop("serialId")
|
||||||
|
user_print_detail["printDate"] = datetime.strptime(
|
||||||
|
user_print_detail["printDate"], '%Y-%m-%d'
|
||||||
|
)
|
||||||
|
|
||||||
|
# add the entry to the user print table with the random serialId
|
||||||
|
self.data.item.put_user_print_detail(
|
||||||
|
data["userId"],
|
||||||
|
serial_id,
|
||||||
|
user_print_detail
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"returnCode": 1,
|
||||||
|
"serialId": serial_id,
|
||||||
|
"apiName": "CMUpsertUserPrintApi"
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_cm_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
||||||
|
upsert = data["cmUpsertUserAll"]
|
||||||
|
user_id = data["userId"]
|
||||||
|
|
||||||
|
if "userData" in upsert and len(upsert["userData"]) > 0:
|
||||||
|
# check if the profile is a bright memory profile
|
||||||
|
p = self.data.profile.get_profile_data(data["userId"], self.version+1)
|
||||||
|
if p is not None:
|
||||||
|
# save the bright memory profile
|
||||||
|
self.data.profile.put_profile_data(
|
||||||
|
user_id, self.version+1, upsert["userData"][0])
|
||||||
|
else:
|
||||||
|
# save the bright profile
|
||||||
|
self.data.profile.put_profile_data(
|
||||||
|
user_id, self.version, upsert["userData"][0])
|
||||||
|
|
||||||
|
if "userActivityList" in upsert:
|
||||||
|
for act in upsert["userActivityList"]:
|
||||||
|
self.data.profile.put_profile_activity(
|
||||||
|
user_id, act["kind"], act["id"], act["sortNumber"],
|
||||||
|
act["param1"], act["param2"], act["param3"], act["param4"])
|
||||||
|
|
||||||
|
if "userItemList" in upsert:
|
||||||
|
for x in upsert["userItemList"]:
|
||||||
|
self.data.item.put_item(user_id, x)
|
||||||
|
|
||||||
|
if "userCardList" in upsert:
|
||||||
|
for x in upsert["userCardList"]:
|
||||||
|
self.data.item.put_card(user_id, x)
|
||||||
|
|
||||||
|
return {'returnCode': 1, 'apiName': 'cmUpsertUserAll'}
|
||||||
|
@ -1,17 +1,31 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
|
|
||||||
|
|
||||||
class OngekiServerConfig():
|
class OngekiServerConfig():
|
||||||
def __init__(self, parent_config: "OngekiConfig") -> None:
|
def __init__(self, parent_config: "OngekiConfig") -> None:
|
||||||
self.__config = parent_config
|
self.__config = parent_config
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enable(self) -> bool:
|
def enable(self) -> bool:
|
||||||
return CoreConfig.get_config_field(self.__config, 'ongeki', 'server', 'enable', default=True)
|
return CoreConfig.get_config_field(self.__config, 'ongeki', 'server', 'enable', default=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def loglevel(self) -> int:
|
def loglevel(self) -> int:
|
||||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'ongeki', 'server', 'loglevel', default="info"))
|
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'ongeki', 'server', 'loglevel', default="info"))
|
||||||
|
|
||||||
|
|
||||||
|
class OngekiGachaConfig():
|
||||||
|
def __init__(self, parent_config: "OngekiConfig") -> None:
|
||||||
|
self.__config = parent_config
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled_gachas(self) -> List[int]:
|
||||||
|
return CoreConfig.get_config_field(self.__config, 'ongeki', 'gachas', 'enabled_gachas', default=[])
|
||||||
|
|
||||||
|
|
||||||
class OngekiConfig(dict):
|
class OngekiConfig(dict):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.server = OngekiServerConfig(self)
|
self.server = OngekiServerConfig(self)
|
||||||
|
self.gachas = OngekiGachaConfig(self)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from typing import Final, Dict
|
from typing import Final, Dict
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
class OngekiConstants():
|
class OngekiConstants():
|
||||||
GAME_CODE = "SDDT"
|
GAME_CODE = "SDDT"
|
||||||
|
|
||||||
@ -35,6 +37,20 @@ class OngekiConstants():
|
|||||||
'SilverJewelEvent',
|
'SilverJewelEvent',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
class CM_GACHA_KINDS(Enum):
|
||||||
|
Normal = 0
|
||||||
|
Pickup = 1
|
||||||
|
BonusRestored = 2
|
||||||
|
Free = 3
|
||||||
|
PickupBonusRestored = 4
|
||||||
|
|
||||||
|
class RARITY_TYPES(Enum):
|
||||||
|
N = 0
|
||||||
|
R = 1
|
||||||
|
SR = 2
|
||||||
|
SSR = 3
|
||||||
|
SRPlus = 12
|
||||||
|
|
||||||
# The game expects the server to give Lunatic an ID of 10, while the game uses 4 internally... except in Music.xml
|
# The game expects the server to give Lunatic an ID of 10, while the game uses 4 internally... except in Music.xml
|
||||||
class DIFF_NAME(Enum):
|
class DIFF_NAME(Enum):
|
||||||
Basic = 0
|
Basic = 0
|
||||||
@ -48,4 +64,4 @@ class OngekiConstants():
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def game_ver_to_string(cls, ver: int):
|
def game_ver_to_string(cls, ver: int):
|
||||||
return cls.VERSION_NAMES[ver]
|
return cls.VERSION_NAMES[ver]
|
||||||
|
@ -2,6 +2,7 @@ from typing import Dict, Optional, List
|
|||||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
||||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON
|
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON
|
||||||
from sqlalchemy.schema import ForeignKey
|
from sqlalchemy.schema import ForeignKey
|
||||||
|
from sqlalchemy.engine import Row
|
||||||
from sqlalchemy.sql import func, select
|
from sqlalchemy.sql import func, select
|
||||||
from sqlalchemy.dialects.mysql import insert
|
from sqlalchemy.dialects.mysql import insert
|
||||||
|
|
||||||
@ -242,6 +243,63 @@ tech_event = Table(
|
|||||||
mysql_charset='utf8mb4'
|
mysql_charset='utf8mb4'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
gacha = Table(
|
||||||
|
"ongeki_user_gacha",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
|
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||||
|
Column("gachaId", Integer, nullable=False),
|
||||||
|
Column("totalGachaCnt", Integer, server_default="0"),
|
||||||
|
Column("ceilingGachaCnt", Integer, server_default="0"),
|
||||||
|
Column("selectPoint", Integer, server_default="0"),
|
||||||
|
Column("useSelectPoint", Integer, server_default="0"),
|
||||||
|
Column("dailyGachaCnt", Integer, server_default="0"),
|
||||||
|
Column("fiveGachaCnt", Integer, server_default="0"),
|
||||||
|
Column("elevenGachaCnt", Integer, server_default="0"),
|
||||||
|
Column("dailyGachaDate", TIMESTAMP, nullable=False, server_default=func.now()),
|
||||||
|
UniqueConstraint("user", "gachaId", name="ongeki_user_gacha_uk"),
|
||||||
|
mysql_charset='utf8mb4'
|
||||||
|
)
|
||||||
|
|
||||||
|
gacha_supply = Table(
|
||||||
|
"ongeki_user_gacha_supply",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
|
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||||
|
Column("cardId", Integer, nullable=False),
|
||||||
|
UniqueConstraint("user", "cardId", name="ongeki_user_gacha_supply_uk"),
|
||||||
|
mysql_charset='utf8mb4'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
print_detail = Table(
|
||||||
|
"ongeki_user_print_detail",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
|
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||||
|
Column("cardId", Integer, nullable=False),
|
||||||
|
Column("cardType", Integer, server_default="0"),
|
||||||
|
Column("printDate", TIMESTAMP, nullable=False),
|
||||||
|
Column("serialId", String(20), nullable=False),
|
||||||
|
Column("placeId", Integer, nullable=False),
|
||||||
|
Column("clientId", String(11), nullable=False),
|
||||||
|
Column("printerSerialId", String(20), nullable=False),
|
||||||
|
Column("isHolograph", Boolean, server_default="0"),
|
||||||
|
Column("isAutographed", Boolean, server_default="0"),
|
||||||
|
Column("printOption1", Boolean, server_default="1"),
|
||||||
|
Column("printOption2", Boolean, server_default="1"),
|
||||||
|
Column("printOption3", Boolean, server_default="1"),
|
||||||
|
Column("printOption4", Boolean, server_default="1"),
|
||||||
|
Column("printOption5", Boolean, server_default="1"),
|
||||||
|
Column("printOption6", Boolean, server_default="1"),
|
||||||
|
Column("printOption7", Boolean, server_default="1"),
|
||||||
|
Column("printOption8", Boolean, server_default="1"),
|
||||||
|
Column("printOption9", Boolean, server_default="1"),
|
||||||
|
Column("printOption10", Boolean, server_default="0"),
|
||||||
|
UniqueConstraint("serialId", name="ongeki_user_print_detail_uk"),
|
||||||
|
mysql_charset='utf8mb4'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OngekiItemData(BaseData):
|
class OngekiItemData(BaseData):
|
||||||
def put_card(self, aime_id: int, card_data: Dict) -> Optional[int]:
|
def put_card(self, aime_id: int, card_data: Dict) -> Optional[int]:
|
||||||
@ -545,7 +603,7 @@ class OngekiItemData(BaseData):
|
|||||||
|
|
||||||
if result is None: return None
|
if result is None: return None
|
||||||
return result.fetchall()
|
return result.fetchall()
|
||||||
|
|
||||||
def put_memorychapter(self, aime_id: int, memorychapter_data: Dict) -> Optional[int]:
|
def put_memorychapter(self, aime_id: int, memorychapter_data: Dict) -> Optional[int]:
|
||||||
memorychapter_data["user"] = aime_id
|
memorychapter_data["user"] = aime_id
|
||||||
|
|
||||||
@ -557,10 +615,73 @@ class OngekiItemData(BaseData):
|
|||||||
self.logger.warn(f"put_memorychapter: Failed to update! aime_id: {aime_id}")
|
self.logger.warn(f"put_memorychapter: Failed to update! aime_id: {aime_id}")
|
||||||
return None
|
return None
|
||||||
return result.lastrowid
|
return result.lastrowid
|
||||||
|
|
||||||
def get_memorychapters(self, aime_id: int) -> Optional[List[Dict]]:
|
def get_memorychapters(self, aime_id: int) -> Optional[List[Dict]]:
|
||||||
sql = select(memorychapter).where(memorychapter.c.user == aime_id)
|
sql = select(memorychapter).where(memorychapter.c.user == aime_id)
|
||||||
|
|
||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None: return None
|
if result is None:
|
||||||
return result.fetchall()
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
def get_user_gacha(self, aime_id: int, gacha_id: int) -> Optional[Row]:
|
||||||
|
sql = gacha.select(and_(
|
||||||
|
gacha.c.user == aime_id,
|
||||||
|
gacha.c.gachaId == gacha_id
|
||||||
|
))
|
||||||
|
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchone()
|
||||||
|
|
||||||
|
def get_user_gachas(self, aime_id: int) -> Optional[List[Row]]:
|
||||||
|
sql = gacha.select(gacha.c.user == aime_id)
|
||||||
|
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
def get_user_gacha_supplies(self, aime_id: int) -> Optional[List[Row]]:
|
||||||
|
sql = gacha_supply.select(gacha_supply.c.user == aime_id)
|
||||||
|
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
def put_user_gacha(self, aime_id: int, gacha_id: int, **data) -> Optional[int]:
|
||||||
|
sql = insert(gacha).values(
|
||||||
|
user=aime_id,
|
||||||
|
gachaId=gacha_id,
|
||||||
|
**data)
|
||||||
|
|
||||||
|
conflict = sql.on_duplicate_key_update(
|
||||||
|
user=aime_id,
|
||||||
|
gachaId=gacha_id,
|
||||||
|
**data)
|
||||||
|
result = self.execute(conflict)
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
self.logger.warn(f"put_user_gacha: Failed to insert! aime_id: {aime_id}")
|
||||||
|
return None
|
||||||
|
return result.lastrowid
|
||||||
|
|
||||||
|
def put_user_print_detail(self, aime_id: int, serial_id: str,
|
||||||
|
user_print_data: Dict) -> Optional[int]:
|
||||||
|
sql = insert(print_detail).values(
|
||||||
|
user=aime_id,
|
||||||
|
serialId=serial_id,
|
||||||
|
**user_print_data)
|
||||||
|
|
||||||
|
conflict = sql.on_duplicate_key_update(
|
||||||
|
user=aime_id,
|
||||||
|
serialId=serial_id,
|
||||||
|
**user_print_data)
|
||||||
|
result = self.execute(conflict)
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
self.logger.warn(f"put_user_print_detail: Failed to insert! aime_id: {aime_id}")
|
||||||
|
return None
|
||||||
|
return result.lastrowid
|
||||||
|
@ -78,7 +78,8 @@ profile = Table(
|
|||||||
Column("overDamageBattlePoint", Integer, server_default="0"),
|
Column("overDamageBattlePoint", Integer, server_default="0"),
|
||||||
Column("bestBattlePoint", Integer, server_default="0"),
|
Column("bestBattlePoint", Integer, server_default="0"),
|
||||||
Column("lastEmoneyBrand", Integer, server_default="0"),
|
Column("lastEmoneyBrand", Integer, server_default="0"),
|
||||||
Column("isDialogWatchedSuggestMemory", Boolean),
|
Column("lastEmoneyCredit", Integer, server_default="0"),
|
||||||
|
Column("isDialogWatchedSuggestMemory", Boolean, server_default="0"),
|
||||||
UniqueConstraint("user", "version", name="ongeki_profile_profile_uk"),
|
UniqueConstraint("user", "version", name="ongeki_profile_profile_uk"),
|
||||||
mysql_charset='utf8mb4'
|
mysql_charset='utf8mb4'
|
||||||
)
|
)
|
||||||
|
@ -37,7 +37,210 @@ music = Table(
|
|||||||
mysql_charset='utf8mb4'
|
mysql_charset='utf8mb4'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
gachas = Table(
|
||||||
|
"ongeki_static_gachas",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
|
Column("version", Integer, nullable=False),
|
||||||
|
Column("gachaId", Integer, nullable=False),
|
||||||
|
Column("gachaName", String(255), nullable=False),
|
||||||
|
Column("type", Integer, nullable=False, server_default="0"),
|
||||||
|
Column("kind", Integer, nullable=False, server_default="0"),
|
||||||
|
Column("isCeiling", Boolean, server_default="0"),
|
||||||
|
Column("maxSelectPoint", Integer, server_default="0"),
|
||||||
|
Column("ceilingCnt", Integer, server_default="10"),
|
||||||
|
Column("changeRateCnt1", Integer, server_default="0"),
|
||||||
|
Column("changeRateCnt2", Integer, server_default="0"),
|
||||||
|
Column("startDate", TIMESTAMP, server_default="2018-01-01 00:00:00.0"),
|
||||||
|
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("convertEndDate", TIMESTAMP, server_default="2038-01-01 00:00:00.0"),
|
||||||
|
UniqueConstraint("version", "gachaId", "gachaName", name="ongeki_static_gachas_uk"),
|
||||||
|
mysql_charset='utf8mb4'
|
||||||
|
)
|
||||||
|
|
||||||
|
gacha_cards = Table(
|
||||||
|
"ongeki_static_gacha_cards",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
|
Column("gachaId", Integer, nullable=False),
|
||||||
|
Column("cardId", Integer, nullable=False),
|
||||||
|
Column("rarity", Integer, nullable=False),
|
||||||
|
Column("weight", Integer, server_default="1"),
|
||||||
|
Column("isPickup", Boolean, server_default="0"),
|
||||||
|
Column("isSelect", Boolean, server_default="0"),
|
||||||
|
UniqueConstraint("gachaId", "cardId", name="ongeki_static_gacha_cards_uk"),
|
||||||
|
mysql_charset='utf8mb4'
|
||||||
|
)
|
||||||
|
|
||||||
|
cards = Table(
|
||||||
|
"ongeki_static_cards",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
|
Column("version", Integer, nullable=False),
|
||||||
|
Column("cardId", Integer, nullable=False),
|
||||||
|
Column("name", String(255), nullable=False),
|
||||||
|
Column("charaId", Integer, nullable=False),
|
||||||
|
Column("nickName", String(255)),
|
||||||
|
Column("school", String(255), nullable=False),
|
||||||
|
Column("attribute", String(5), nullable=False),
|
||||||
|
Column("gakunen", String(255), nullable=False),
|
||||||
|
Column("rarity", Integer, nullable=False),
|
||||||
|
Column("levelParam", String(255), nullable=False),
|
||||||
|
Column("skillId", Integer, nullable=False),
|
||||||
|
Column("choKaikaSkillId", Integer, nullable=False),
|
||||||
|
Column("cardNumber", String(255)),
|
||||||
|
UniqueConstraint("version", "cardId", name="ongeki_static_cards_uk"),
|
||||||
|
mysql_charset='utf8mb4'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OngekiStaticData(BaseData):
|
class OngekiStaticData(BaseData):
|
||||||
|
def put_card(self, version: int, card_id: int, **card_data) -> Optional[int]:
|
||||||
|
sql = insert(cards).values(
|
||||||
|
version=version,
|
||||||
|
cardId=card_id,
|
||||||
|
**card_data)
|
||||||
|
|
||||||
|
conflict = sql.on_duplicate_key_update(**card_data)
|
||||||
|
|
||||||
|
result = self.execute(conflict)
|
||||||
|
if result is None:
|
||||||
|
self.logger.warn(f"Failed to insert card! card_id {card_id}")
|
||||||
|
return None
|
||||||
|
return result.lastrowid
|
||||||
|
|
||||||
|
def get_card(self, version: int, card_id: int) -> Optional[Dict]:
|
||||||
|
sql = cards.select(and_(
|
||||||
|
cards.c.version <= version,
|
||||||
|
cards.c.cardId == card_id
|
||||||
|
))
|
||||||
|
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchone()
|
||||||
|
|
||||||
|
def get_card_by_card_number(self, version: int, card_number: str) -> Optional[Dict]:
|
||||||
|
if not card_number.startswith("[O.N.G.E.K.I.]"):
|
||||||
|
card_number = f"[O.N.G.E.K.I.]{card_number}"
|
||||||
|
|
||||||
|
sql = cards.select(and_(
|
||||||
|
cards.c.version <= version,
|
||||||
|
cards.c.cardNumber == card_number
|
||||||
|
))
|
||||||
|
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchone()
|
||||||
|
|
||||||
|
def get_card_by_name(self, version: int, name: str) -> Optional[Dict]:
|
||||||
|
sql = cards.select(and_(
|
||||||
|
cards.c.version <= version,
|
||||||
|
cards.c.name == name
|
||||||
|
))
|
||||||
|
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchone()
|
||||||
|
|
||||||
|
def get_cards(self, version: int) -> Optional[List[Dict]]:
|
||||||
|
sql = cards.select(cards.c.version <= version)
|
||||||
|
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
def get_cards_by_rarity(self, version: int, rarity: int) -> Optional[List[Dict]]:
|
||||||
|
sql = cards.select(and_(
|
||||||
|
cards.c.version <= version,
|
||||||
|
cards.c.rarity == rarity
|
||||||
|
))
|
||||||
|
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
def put_gacha(self, version: int, gacha_id: int, gacha_name: int,
|
||||||
|
gacha_kind: int, **gacha_data) -> Optional[int]:
|
||||||
|
sql = insert(gachas).values(
|
||||||
|
version=version,
|
||||||
|
gachaId=gacha_id,
|
||||||
|
gachaName=gacha_name,
|
||||||
|
kind=gacha_kind,
|
||||||
|
**gacha_data
|
||||||
|
)
|
||||||
|
|
||||||
|
conflict = sql.on_duplicate_key_update(
|
||||||
|
version=version,
|
||||||
|
gachaId=gacha_id,
|
||||||
|
gachaName=gacha_name,
|
||||||
|
kind=gacha_kind,
|
||||||
|
**gacha_data
|
||||||
|
)
|
||||||
|
|
||||||
|
result = self.execute(conflict)
|
||||||
|
if result is None:
|
||||||
|
self.logger.warn(f"Failed to insert gacha! gacha_id {gacha_id}")
|
||||||
|
return None
|
||||||
|
return result.lastrowid
|
||||||
|
|
||||||
|
def get_gacha(self, version: int, gacha_id: int) -> Optional[Dict]:
|
||||||
|
sql = gachas.select(and_(
|
||||||
|
gachas.c.version == version,
|
||||||
|
gachas.c.gachaId == gacha_id
|
||||||
|
))
|
||||||
|
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchone()
|
||||||
|
|
||||||
|
def get_gachas(self, version: int) -> Optional[List[Dict]]:
|
||||||
|
sql = gachas.select(
|
||||||
|
gachas.c.version == version).order_by(
|
||||||
|
gachas.c.gachaId.asc()
|
||||||
|
)
|
||||||
|
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
def put_gacha_card(self, gacha_id: int, card_id: int, **gacha_card) -> Optional[int]:
|
||||||
|
sql = insert(gacha_cards).values(
|
||||||
|
gachaId=gacha_id,
|
||||||
|
cardId=card_id,
|
||||||
|
**gacha_card
|
||||||
|
)
|
||||||
|
|
||||||
|
conflict = sql.on_duplicate_key_update(
|
||||||
|
gachaId=gacha_id,
|
||||||
|
cardId=card_id,
|
||||||
|
**gacha_card
|
||||||
|
)
|
||||||
|
|
||||||
|
result = self.execute(conflict)
|
||||||
|
if result is None:
|
||||||
|
self.logger.warn(f"Failed to insert gacha card! gacha_id {gacha_id}")
|
||||||
|
return None
|
||||||
|
return result.lastrowid
|
||||||
|
|
||||||
|
def get_gacha_cards(self, gacha_id: int) -> Optional[List[Dict]]:
|
||||||
|
sql = gacha_cards.select(
|
||||||
|
gacha_cards.c.gachaId == gacha_id
|
||||||
|
)
|
||||||
|
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
def put_event(self, version: int, event_id: int, event_type: int, event_name: str) -> Optional[int]:
|
def put_event(self, version: int, event_id: int, event_type: int, event_name: str) -> Optional[int]:
|
||||||
sql = insert(events).values(
|
sql = insert(events).values(
|
||||||
version = version,
|
version = version,
|
||||||
|
Loading…
Reference in New Issue
Block a user