Merge branch 'develop' into fork_develop
This commit is contained in:
commit
346b74e51b
@ -150,6 +150,12 @@ class DatabaseConfig:
|
||||
default=10000,
|
||||
)
|
||||
|
||||
@property
|
||||
def enable_memcached(self) -> bool:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "core", "database", "enable_memcached", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def memcached_host(self) -> str:
|
||||
return CoreConfig.get_config_field(
|
||||
|
@ -17,7 +17,7 @@ except ModuleNotFoundError:
|
||||
|
||||
def cached(lifetime: int = 10, extra_key: Any = None) -> Callable:
|
||||
def _cached(func: Callable) -> Callable:
|
||||
if has_mc:
|
||||
if has_mc and (cfg and cfg.database.enable_memcached):
|
||||
hostname = "127.0.0.1"
|
||||
if cfg:
|
||||
hostname = cfg.database.memcached_host
|
||||
|
2
core/data/schema/versions/SBZV_5_rollback.sql
Normal file
2
core/data/schema/versions/SBZV_5_rollback.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE diva_profile
|
||||
DROP skn_eqp;
|
2
core/data/schema/versions/SBZV_6_upgrade.sql
Normal file
2
core/data/schema/versions/SBZV_6_upgrade.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE diva_profile
|
||||
ADD skn_eqp INT NOT NULL DEFAULT 0;
|
@ -59,6 +59,28 @@ python read.py --game SDBT --version <version ID> --binfolder /path/to/game/fold
|
||||
|
||||
The importer for Chunithm will import: Events, Music, Charge Items and Avatar Accesories.
|
||||
|
||||
### Config
|
||||
|
||||
Config file is located in `config/chuni.yaml`.
|
||||
|
||||
| Option | Info |
|
||||
|------------------|----------------------------------------------------------------------------------------------------------------|
|
||||
| `news_msg` | If this is set, the news at the top of the main screen will be displayed (up to Chunithm Paradise Lost) |
|
||||
| `name` | If this is set, all players that are not on a team will use this one by default. |
|
||||
| `rank_scale` | Scales the in-game ranking based on the number of teams within the database |
|
||||
| `use_login_bonus`| This is used to enable the login bonuses |
|
||||
| `crypto` | This option is used to enable the TLS Encryption |
|
||||
|
||||
|
||||
**If you would like to use network encryption, the following will be required underneath but key, iv and hash are required:**
|
||||
|
||||
```yaml
|
||||
crypto:
|
||||
encrypted_only: False
|
||||
keys:
|
||||
13: ["0000000000000000000000000000000000000000000000000000000000000000", "00000000000000000000000000000000", "0000000000000000"]
|
||||
```
|
||||
|
||||
### Database upgrade
|
||||
|
||||
Always make sure your database (tables) are up-to-date, to do so go to the `core/data/schema/versions` folder and see
|
||||
@ -88,6 +110,36 @@ After a failed Online Battle the room will be deleted. The host is used for the
|
||||
- Timer countdown should be handled globally and not by one user
|
||||
- Game can freeze or can crash if someone (especially the host) leaves the matchmaking
|
||||
|
||||
### Rivals
|
||||
|
||||
You can configure up to 4 rivals in Chunithm on a per-user basis. There is no UI to do this currently, so in the database, you can do this:
|
||||
```sql
|
||||
INSERT INTO aime.chuni_item_favorite (user, version, favId, favKind) VALUES (<user1>, <version>, <user2>, 2);
|
||||
INSERT INTO aime.chuni_item_favorite (user, version, favId, favKind) VALUES (<user2>, <version>, <user1>, 2);
|
||||
```
|
||||
Note that the version **must match**, otherwise song lookup may not work.
|
||||
|
||||
### Teams
|
||||
|
||||
You can also configure teams for users to be on. There is no UI to do this currently, so in the database, you can do this:
|
||||
```sql
|
||||
INSERT INTO aime.chuni_profile_team (teamName) VALUES (<teamName>);
|
||||
```
|
||||
Team names can be regular ASCII, and they will be displayed ingame.
|
||||
|
||||
On smaller installations, you may also wish to enable scaled team rankings. By default, Chunithm determines team ranking within the first 100 teams. This can be configured in the YAML:
|
||||
```yaml
|
||||
team:
|
||||
rank_scale: True # Scales the in-game ranking based on the number of teams within the database, rather than the default scale of ~100 that the game normally uses.
|
||||
```
|
||||
|
||||
### Favorite songs
|
||||
You can set the songs that will be in a user's Favorite Songs category using the following SQL entries:
|
||||
```sql
|
||||
INSERT INTO aime.chuni_item_favorite (user, version, favId, favKind) VALUES (<user>, <version>, <songId>, 1);
|
||||
```
|
||||
The songId is based on the actual ID within your version of Chunithm.
|
||||
|
||||
|
||||
## crossbeats REV.
|
||||
|
||||
@ -211,6 +263,9 @@ Config file is located in `config/diva.yaml`.
|
||||
| `unlock_all_modules` | Unlocks all modules (costumes) by default, if set to `False` all modules need to be purchased |
|
||||
| `unlock_all_items` | Unlocks all items (customizations) by default, if set to `False` all items need to be purchased |
|
||||
|
||||
### Custom PV Lists (databanks)
|
||||
|
||||
In order to use custom PV Lists, simply drop in your .dat files inside of /titles/diva/data/ and make sure they are called PvList0.dat, PvList1.dat, PvList2.dat, PvList3.dat and PvList4.dat exactly.
|
||||
|
||||
### Database upgrade
|
||||
|
||||
@ -257,9 +312,19 @@ Config file is located in `config/ongeki.yaml`.
|
||||
| Option | Info |
|
||||
|------------------|----------------------------------------------------------------------------------------------------------------|
|
||||
| `enabled_gachas` | Enter all gacha IDs for Card Maker to work, other than default may not work due to missing cards added to them |
|
||||
| `crypto` | This option is used to enable the TLS Encryption |
|
||||
|
||||
Note: 1149 and higher are only for Card Maker 1.35 and higher and will be ignored on lower versions.
|
||||
|
||||
**If you would like to use network encryption, the following will be required underneath but key, iv and hash are required:**
|
||||
|
||||
```yaml
|
||||
crypto:
|
||||
encrypted_only: False
|
||||
keys:
|
||||
7: ["0000000000000000000000000000000000000000000000000000000000000000", "00000000000000000000000000000000", "0000000000000000"]
|
||||
```
|
||||
|
||||
### Database upgrade
|
||||
|
||||
Always make sure your database (tables) are up-to-date, to do so go to the `core/data/schema/versions` folder and see
|
||||
@ -509,6 +574,8 @@ python dbutils.py --game SDEW upgrade
|
||||
- Player title is currently static and cannot be changed in-game
|
||||
- QR Card Scanning currently only load a static hero
|
||||
|
||||
**Network hashing in GssSite.dll must be disabled**
|
||||
|
||||
### Credits for SAO support:
|
||||
|
||||
- Midorica - Limited Network Support
|
||||
|
@ -1,9 +1,11 @@
|
||||
server:
|
||||
enable: True
|
||||
loglevel: "info"
|
||||
news_msg: ""
|
||||
|
||||
team:
|
||||
name: ARTEMiS
|
||||
name: ARTEMiS # If this is set, all players that are not on a team will use this one by default.
|
||||
rank_scale: True # Scales the in-game ranking based on the number of teams within the database, rather than the default scale of ~100 that the game normally uses.
|
||||
|
||||
mods:
|
||||
use_login_bonus: True
|
||||
|
@ -24,6 +24,7 @@ database:
|
||||
sha2_password: False
|
||||
loglevel: "warn"
|
||||
user_table_autoincrement_start: 10000
|
||||
enable_memcached: True
|
||||
memcached_host: "localhost"
|
||||
|
||||
frontend:
|
||||
|
@ -181,7 +181,17 @@ class ChuniBase:
|
||||
return {"type": data["type"], "length": 0, "gameIdlistList": []}
|
||||
|
||||
def handle_get_game_message_api_request(self, data: Dict) -> Dict:
|
||||
return {"type": data["type"], "length": "0", "gameMessageList": []}
|
||||
return {
|
||||
"type": data["type"],
|
||||
"length": 1,
|
||||
"gameMessageList": [{
|
||||
"id": 1,
|
||||
"type": 1,
|
||||
"message": f"Welcome to {self.core_cfg.server.name} network!" if not self.game_cfg.server.news_msg else self.game_cfg.server.news_msg,
|
||||
"startDate": "2017-12-05 07:00:00.0",
|
||||
"endDate": "2099-12-31 00:00:00.0"
|
||||
}]
|
||||
}
|
||||
|
||||
def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
|
||||
return {"type": data["type"], "gameRankingList": []}
|
||||
@ -361,11 +371,98 @@ class ChuniBase:
|
||||
"userDuelList": duel_list,
|
||||
}
|
||||
|
||||
def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
|
||||
p = self.data.profile.get_rival(data["rivalId"])
|
||||
if p is None:
|
||||
return {}
|
||||
userRivalData = {
|
||||
"rivalId": p.user,
|
||||
"rivalName": p.userName
|
||||
}
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"userRivalData": userRivalData
|
||||
}
|
||||
|
||||
def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
|
||||
m = self.data.score.get_rival_music(data["rivalId"], data["nextIndex"], data["maxCount"])
|
||||
if m is None:
|
||||
return {}
|
||||
|
||||
user_rival_music_list = []
|
||||
for music in m:
|
||||
actual_music_id = self.data.static.get_song(music["musicId"])
|
||||
if actual_music_id is None:
|
||||
music_id = music["musicId"]
|
||||
else:
|
||||
music_id = actual_music_id["songId"]
|
||||
level = music["level"]
|
||||
score = music["score"]
|
||||
rank = music["rank"]
|
||||
|
||||
# Find the existing entry for the current musicId in the user_rival_music_list
|
||||
music_entry = next((entry for entry in user_rival_music_list if entry["musicId"] == music_id), None)
|
||||
|
||||
if music_entry is None:
|
||||
# If the entry doesn't exist, create a new entry
|
||||
music_entry = {
|
||||
"musicId": music_id,
|
||||
"length": 0,
|
||||
"userRivalMusicDetailList": []
|
||||
}
|
||||
user_rival_music_list.append(music_entry)
|
||||
|
||||
# Check if the current score is higher than the previous highest score for the level
|
||||
level_entry = next(
|
||||
(
|
||||
entry
|
||||
for entry in music_entry["userRivalMusicDetailList"]
|
||||
if entry["level"] == level
|
||||
),
|
||||
None,
|
||||
)
|
||||
if level_entry is None or score > level_entry["scoreMax"]:
|
||||
# If the level entry doesn't exist or the score is higher, update or add the entry
|
||||
level_entry = {
|
||||
"level": level,
|
||||
"scoreMax": score,
|
||||
"scoreRank": rank
|
||||
}
|
||||
|
||||
if level_entry not in music_entry["userRivalMusicDetailList"]:
|
||||
music_entry["userRivalMusicDetailList"].append(level_entry)
|
||||
|
||||
music_entry["length"] = len(music_entry["userRivalMusicDetailList"])
|
||||
|
||||
result = {
|
||||
"userId": data["userId"],
|
||||
"rivalId": data["rivalId"],
|
||||
"nextIndex": -1,
|
||||
"userRivalMusicList": user_rival_music_list
|
||||
}
|
||||
|
||||
return result
|
||||
def handle_get_user_rival_music_api_requestded(self, data: Dict) -> Dict:
|
||||
m = self.data.score.get_rival_music(data["rivalId"], data["nextIndex"], data["maxCount"])
|
||||
if m is None:
|
||||
return {}
|
||||
|
||||
userRivalMusicList = []
|
||||
for music in m:
|
||||
self.logger.debug(music["point"])
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"rivalId": data["rivalId"],
|
||||
"nextIndex": -1
|
||||
|
||||
}
|
||||
|
||||
def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
|
||||
user_fav_item_list = []
|
||||
|
||||
# still needs to be implemented on WebUI
|
||||
# 1: Music, 3: Character
|
||||
# 1: Music, 2: User, 3: Character
|
||||
fav_list = self.data.item.get_all_favorites(
|
||||
data["userId"], self.version, fav_kind=int(data["kind"])
|
||||
)
|
||||
@ -501,7 +598,7 @@ class ChuniBase:
|
||||
if len(song_list) >= max_ct:
|
||||
break
|
||||
|
||||
if len(song_list) >= next_idx + max_ct:
|
||||
if len(music_detail) >= next_idx + max_ct:
|
||||
next_idx += max_ct
|
||||
else:
|
||||
next_idx = -1
|
||||
@ -600,25 +697,43 @@ class ChuniBase:
|
||||
}
|
||||
|
||||
def handle_get_user_team_api_request(self, data: Dict) -> Dict:
|
||||
# TODO: use the database "chuni_profile_team" with a GUI
|
||||
# Default values
|
||||
team_id = 65535
|
||||
team_name = self.game_cfg.team.team_name
|
||||
if team_name == "":
|
||||
team_rank = 0
|
||||
|
||||
# Get user profile
|
||||
profile = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||
if profile and profile["teamId"]:
|
||||
# Get team by id
|
||||
team = self.data.profile.get_team_by_id(profile["teamId"])
|
||||
|
||||
if team:
|
||||
team_id = team["id"]
|
||||
team_name = team["teamName"]
|
||||
# Determine whether to use scaled ranks, or original system
|
||||
if self.game_cfg.team.rank_scale:
|
||||
team_rank = self.data.profile.get_team_rank(team["id"])
|
||||
else:
|
||||
team_rank = self.data.profile.get_team_rank_actual(team["id"])
|
||||
|
||||
# Don't return anything if no team name has been defined for defaults and there is no team set for the player
|
||||
if not profile["teamId"] and team_name == "":
|
||||
return {"userId": data["userId"], "teamId": 0}
|
||||
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
"teamId": 1,
|
||||
"teamRank": 1,
|
||||
"teamId": team_id,
|
||||
"teamRank": team_rank,
|
||||
"teamName": team_name,
|
||||
"userTeamPoint": {
|
||||
"userId": data["userId"],
|
||||
"teamId": 1,
|
||||
"teamId": team_id,
|
||||
"orderId": 1,
|
||||
"teamPoint": 1,
|
||||
"aggrDate": data["playDate"],
|
||||
},
|
||||
}
|
||||
|
||||
def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict:
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
@ -709,9 +824,25 @@ class ChuniBase:
|
||||
self.data.score.put_playlog(user_id, playlog)
|
||||
|
||||
if "userTeamPoint" in upsert:
|
||||
# TODO: team stuff
|
||||
pass
|
||||
team_points = upsert["userTeamPoint"]
|
||||
try:
|
||||
for tp in team_points:
|
||||
if tp["teamId"] != '65535':
|
||||
# Fetch the current team data
|
||||
current_team = self.data.profile.get_team_by_id(tp["teamId"])
|
||||
|
||||
# Calculate the new teamPoint
|
||||
new_team_point = int(tp["teamPoint"]) + current_team["teamPoint"]
|
||||
|
||||
# Prepare the data to update
|
||||
team_data = {
|
||||
"teamPoint": new_team_point
|
||||
}
|
||||
|
||||
# Update the team data
|
||||
self.data.profile.update_team(tp["teamId"], team_data)
|
||||
except:
|
||||
pass # Probably a better way to catch if the team is not set yet (new profiles), but let's just pass
|
||||
if "userMapAreaList" in upsert:
|
||||
for map_area in upsert["userMapAreaList"]:
|
||||
self.data.item.put_map_area(user_id, map_area)
|
||||
|
@ -20,6 +20,12 @@ class ChuniServerConfig:
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def news_msg(self) -> str:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "chuni", "server", "news_msg", default=""
|
||||
)
|
||||
|
||||
|
||||
class ChuniTeamConfig:
|
||||
def __init__(self, parent_config: "ChuniConfig") -> None:
|
||||
@ -30,6 +36,11 @@ class ChuniTeamConfig:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "chuni", "team", "name", default=""
|
||||
)
|
||||
@property
|
||||
def rank_scale(self) -> str:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "chuni", "team", "rank_scale", default="False"
|
||||
)
|
||||
|
||||
|
||||
class ChuniModsConfig:
|
||||
|
@ -637,3 +637,103 @@ class ChuniProfileData(BaseData):
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def get_team_by_id(self, team_id: int) -> Optional[Row]:
|
||||
sql = select(team).where(team.c.id == team_id)
|
||||
result = self.execute(sql)
|
||||
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_team_rank_actual(self, team_id: int) -> int:
|
||||
# Normal ranking system, likely the one used in the real servers
|
||||
# Query all teams sorted by 'teamPoint'
|
||||
result = self.execute(
|
||||
select(team.c.id).order_by(team.c.teamPoint.desc())
|
||||
)
|
||||
|
||||
# Get the rank of the team with the given team_id
|
||||
rank = None
|
||||
for i, row in enumerate(result, start=1):
|
||||
if row.id == team_id:
|
||||
rank = i
|
||||
break
|
||||
|
||||
# Return the rank if found, or a default rank otherwise
|
||||
return rank if rank is not None else 0
|
||||
|
||||
def get_team_rank(self, team_id: int) -> int:
|
||||
# Scaled ranking system, designed for smaller instances.
|
||||
# Query all teams sorted by 'teamPoint'
|
||||
result = self.execute(
|
||||
select(team.c.id).order_by(team.c.teamPoint.desc())
|
||||
)
|
||||
|
||||
# Count total number of teams
|
||||
total_teams = self.execute(select(func.count()).select_from(team)).scalar()
|
||||
|
||||
# Get the rank of the team with the given team_id
|
||||
rank = None
|
||||
for i, row in enumerate(result, start=1):
|
||||
if row.id == team_id:
|
||||
rank = i
|
||||
break
|
||||
|
||||
# If the team is not found, return default rank
|
||||
if rank is None:
|
||||
return 0
|
||||
|
||||
# Define rank tiers
|
||||
tiers = {
|
||||
1: range(1, int(total_teams * 0.1) + 1), # Rainbow
|
||||
2: range(int(total_teams * 0.1) + 1, int(total_teams * 0.4) + 1), # Gold
|
||||
3: range(int(total_teams * 0.4) + 1, int(total_teams * 0.7) + 1), # Silver
|
||||
4: range(int(total_teams * 0.7) + 1, total_teams + 1), # Grey
|
||||
}
|
||||
|
||||
# Assign rank based on tier
|
||||
for tier_rank, tier_range in tiers.items():
|
||||
if rank in tier_range:
|
||||
return tier_rank
|
||||
|
||||
# Return default rank if not found in any tier
|
||||
return 0
|
||||
|
||||
def update_team(self, team_id: int, team_data: Dict) -> bool:
|
||||
team_data["id"] = team_id
|
||||
|
||||
sql = insert(team).values(**team_data)
|
||||
conflict = sql.on_duplicate_key_update(**team_data)
|
||||
|
||||
result = self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(
|
||||
f"update_team: Failed to update team! team id: {team_id}"
|
||||
)
|
||||
return False
|
||||
return True
|
||||
def get_rival(self, rival_id: int) -> Optional[Row]:
|
||||
sql = select(profile).where(profile.c.user == rival_id)
|
||||
result = self.execute(sql)
|
||||
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
def get_overview(self) -> Dict:
|
||||
# Fetch and add up all the playcounts
|
||||
playcount_sql = self.execute(select(profile.c.playCount))
|
||||
|
||||
if playcount_sql is None:
|
||||
self.logger.warn(
|
||||
f"get_overview: Couldn't pull playcounts"
|
||||
)
|
||||
return 0
|
||||
|
||||
total_play_count = 0;
|
||||
for row in playcount_sql:
|
||||
total_play_count += row[0]
|
||||
return {
|
||||
"total_play_count": total_play_count
|
||||
}
|
@ -200,3 +200,10 @@ class ChuniScoreData(BaseData):
|
||||
if result is None:
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def get_rival_music(self, rival_id: int, index: int, max_count: int) -> Optional[List[Dict]]:
|
||||
sql = select(playlog).where(playlog.c.user == rival_id).limit(max_count).offset(index)
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
@ -453,6 +453,15 @@ class ChuniStaticData(BaseData):
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_song(self, music_id: int) -> Optional[Row]:
|
||||
sql = music.select(music.c.id == music_id)
|
||||
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
|
||||
def put_avatar(
|
||||
self,
|
||||
version: int,
|
||||
|
@ -7,4 +7,4 @@ index = DivaServlet
|
||||
database = DivaData
|
||||
reader = DivaReader
|
||||
game_codes = [DivaConstants.GAME_CODE]
|
||||
current_schema_version = 5
|
||||
current_schema_version = 6
|
||||
|
@ -402,7 +402,7 @@ class DivaBase:
|
||||
response += f"&lv_num={profile['lv_num']}"
|
||||
response += f"&lv_pnt={profile['lv_pnt']}"
|
||||
response += f"&vcld_pts={profile['vcld_pts']}"
|
||||
response += f"&skn_eqp={profile['use_pv_skn_eqp']}"
|
||||
response += f"&skn_eqp={profile['skn_eqp']}"
|
||||
response += f"&btn_se_eqp={profile['btn_se_eqp']}"
|
||||
response += f"&sld_se_eqp={profile['sld_se_eqp']}"
|
||||
response += f"&chn_sld_se_eqp={profile['chn_sld_se_eqp']}"
|
||||
|
@ -51,6 +51,7 @@ profile = Table(
|
||||
Column("rgo_sts", Integer, nullable=False, server_default="1"),
|
||||
Column("lv_efct_id", Integer, nullable=False, server_default="0"),
|
||||
Column("lv_plt_id", Integer, nullable=False, server_default="1"),
|
||||
Column("skn_eqp", Integer, nullable=False, server_default="0"),
|
||||
Column("passwd_stat", Integer, nullable=False, server_default="0"),
|
||||
Column("passwd", String(12), nullable=False, server_default="**********"),
|
||||
Column(
|
||||
|
@ -157,7 +157,6 @@ class PokkenBase:
|
||||
support_set_3
|
||||
aid_skill_list
|
||||
achievement_flag
|
||||
pokemon_data
|
||||
event_achievement_flag
|
||||
event_achievement_param
|
||||
"""
|
||||
@ -200,7 +199,7 @@ class PokkenBase:
|
||||
load_usr.home_loc_name = profile_dict.get("home_loc_name", "")
|
||||
load_usr.pref_code = profile_dict.get("pref_code", 0)
|
||||
load_usr.trainer_name = profile_dict.get(
|
||||
"trainer_name", "Newb" + str(random.randint(1111, 999999))
|
||||
"trainer_name", f"Newb{str(user_id).zfill(4)}"
|
||||
)
|
||||
load_usr.trainer_rank_point = profile_dict.get("trainer_rank_point", 0)
|
||||
load_usr.wallet = profile_dict.get("wallet", 0)
|
||||
@ -262,6 +261,29 @@ class PokkenBase:
|
||||
load_usr.sp_bonus_key_value_2 = profile_dict.get("sp_bonus_key_value_2", 0)
|
||||
load_usr.last_play_event_id = profile_dict.get("last_play_event_id", 0)
|
||||
|
||||
if pokemon_data is not None:
|
||||
for pkmn in pokemon_data:
|
||||
pkmn_d = pkmn._asdict()
|
||||
pkm = jackal_pb2.LoadUserResponseData.PokemonData()
|
||||
|
||||
pkm.char_id = pkmn_d.get('char_id', 0)
|
||||
pkm.illustration_book_no = pkmn_d.get('illustration_book_no', 0)
|
||||
pkm.pokemon_exp = pkmn_d.get('pokemon_exp', 0)
|
||||
pkm.battle_num_vs_wan = pkmn_d.get('battle_num_vs_wan', 0)
|
||||
pkm.win_vs_wan = pkmn_d.get('win_vs_wan', 0)
|
||||
pkm.battle_num_vs_lan = pkmn_d.get('battle_num_vs_lan', 0)
|
||||
pkm.win_vs_lan = pkmn_d.get('win_vs_lan', 0)
|
||||
pkm.battle_num_vs_cpu = pkmn_d.get('battle_num_vs_cpu', 0)
|
||||
pkm.win_cpu = pkmn_d.get('win_cpu', 0)
|
||||
pkm.battle_all_num_tutorial = pkmn_d.get('battle_all_num_tutorial', 0)
|
||||
pkm.battle_num_tutorial = pkmn_d.get('battle_num_tutorial', 0)
|
||||
pkm.bp_point_atk = pkmn_d.get('bp_point_atk', 0)
|
||||
pkm.bp_point_res = pkmn_d.get('bp_point_res', 0)
|
||||
pkm.bp_point_def = pkmn_d.get('bp_point_def', 0)
|
||||
pkm.bp_point_sp = pkmn_d.get('bp_point_sp', 0)
|
||||
|
||||
load_usr.pokemon_data.append(pkm)
|
||||
|
||||
res.load_user.CopyFrom(load_usr)
|
||||
return res.SerializeToString()
|
||||
|
||||
@ -325,7 +347,7 @@ class PokkenBase:
|
||||
for evt_param in req.event_achievement_param:
|
||||
evt_params.append(evt_param)
|
||||
|
||||
self.data.profile.update_profile_event(user_id, evt_state, evt_flgs, evt_params, )
|
||||
self.data.profile.update_profile_event(user_id, evt_state, evt_flgs, evt_params, req.last_play_event_id)
|
||||
|
||||
for reward in req.reward_data:
|
||||
self.data.item.add_reward(user_id, reward.get_category_id, reward.get_content_id, reward.get_type_id)
|
||||
|
@ -3,6 +3,7 @@ from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, an
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select, update, delete
|
||||
from sqlalchemy.sql.functions import coalesce
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
@ -257,18 +258,27 @@ class PokkenProfileData(BaseData):
|
||||
user=user_id,
|
||||
char_id=pokemon_id,
|
||||
illustration_book_no=illust_no,
|
||||
bp_point_atk=atk,
|
||||
bp_point_res=res,
|
||||
bp_point_def=defe,
|
||||
bp_point_sp=sp,
|
||||
pokemon_exp=0,
|
||||
battle_num_vs_wan=0,
|
||||
win_vs_wan=0,
|
||||
battle_num_vs_lan=0,
|
||||
win_vs_lan=0,
|
||||
battle_num_vs_cpu=0,
|
||||
win_cpu=0,
|
||||
battle_all_num_tutorial=0,
|
||||
battle_num_tutorial=0,
|
||||
bp_point_atk=1+atk,
|
||||
bp_point_res=1+res,
|
||||
bp_point_def=1+defe,
|
||||
bp_point_sp=1+sp,
|
||||
)
|
||||
|
||||
conflict = sql.on_duplicate_key_update(
|
||||
illustration_book_no=illust_no,
|
||||
bp_point_atk=atk,
|
||||
bp_point_res=res,
|
||||
bp_point_def=defe,
|
||||
bp_point_sp=sp,
|
||||
bp_point_atk=pokemon_data.c.bp_point_atk + atk,
|
||||
bp_point_res=pokemon_data.c.bp_point_res + res,
|
||||
bp_point_def=pokemon_data.c.bp_point_def + defe,
|
||||
bp_point_sp=pokemon_data.c.bp_point_sp + sp,
|
||||
)
|
||||
|
||||
result = self.execute(conflict)
|
||||
@ -284,7 +294,7 @@ class PokkenProfileData(BaseData):
|
||||
xp: int
|
||||
) -> None:
|
||||
sql = update(pokemon_data).where(and_(pokemon_data.c.user==user_id, pokemon_data.c.char_id==pokemon_id)).values(
|
||||
pokemon_exp=pokemon_data.c.pokemon_exp + xp
|
||||
pokemon_exp=coalesce(pokemon_data.c.pokemon_exp, 0) + xp
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
@ -292,29 +302,38 @@ class PokkenProfileData(BaseData):
|
||||
self.logger.warning(f"Failed to add {xp} XP to pokemon ID {pokemon_id} for user {user_id}")
|
||||
|
||||
def get_pokemon_data(self, user_id: int, pokemon_id: int) -> Optional[Row]:
|
||||
pass
|
||||
sql = pokemon_data.select(and_(pokemon_data.c.user == user_id, pokemon_data.c.char_id == pokemon_id))
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
def get_all_pokemon_data(self, user_id: int) -> Optional[List[Row]]:
|
||||
pass
|
||||
sql = pokemon_data.select(pokemon_data.c.user == user_id)
|
||||
result = self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
def put_pokemon_battle_result(
|
||||
self, user_id: int, pokemon_id: int, match_type: PokkenConstants.BATTLE_TYPE, match_result: PokkenConstants.BATTLE_RESULT
|
||||
) -> None:
|
||||
"""
|
||||
Records the match stats (type and win/loss) for the pokemon and profile
|
||||
coalesce(pokemon_data.c.win_vs_wan, 0)
|
||||
"""
|
||||
sql = update(pokemon_data).where(and_(pokemon_data.c.user==user_id, pokemon_data.c.char_id==pokemon_id)).values(
|
||||
battle_num_tutorial=pokemon_data.c.battle_num_tutorial + 1 if match_type==PokkenConstants.BATTLE_TYPE.TUTORIAL else pokemon_data.c.battle_num_tutorial,
|
||||
battle_all_num_tutorial=pokemon_data.c.battle_all_num_tutorial + 1 if match_type==PokkenConstants.BATTLE_TYPE.TUTORIAL else pokemon_data.c.battle_all_num_tutorial,
|
||||
battle_num_tutorial=coalesce(pokemon_data.c.battle_num_tutorial, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.TUTORIAL else coalesce(pokemon_data.c.battle_num_tutorial, 0),
|
||||
battle_all_num_tutorial=coalesce(pokemon_data.c.battle_all_num_tutorial, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.TUTORIAL else coalesce(pokemon_data.c.battle_all_num_tutorial, 0),
|
||||
|
||||
battle_num_vs_cpu=pokemon_data.c.battle_num_vs_cpu + 1 if match_type==PokkenConstants.BATTLE_TYPE.AI else pokemon_data.c.battle_num_vs_cpu,
|
||||
win_cpu=pokemon_data.c.win_cpu + 1 if match_type==PokkenConstants.BATTLE_TYPE.AI and match_result==PokkenConstants.BATTLE_RESULT.WIN else pokemon_data.c.win_cpu,
|
||||
battle_num_vs_cpu=coalesce(pokemon_data.c.battle_num_vs_cpu, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.AI else coalesce(pokemon_data.c.battle_num_vs_cpu, 0),
|
||||
win_cpu=coalesce(pokemon_data.c.win_cpu, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.AI and match_result==PokkenConstants.BATTLE_RESULT.WIN else coalesce(pokemon_data.c.win_cpu, 0),
|
||||
|
||||
battle_num_vs_lan=pokemon_data.c.battle_num_vs_lan + 1 if match_type==PokkenConstants.BATTLE_TYPE.LAN else pokemon_data.c.battle_num_vs_lan,
|
||||
win_vs_lan=pokemon_data.c.win_vs_lan + 1 if match_type==PokkenConstants.BATTLE_TYPE.LAN and match_result==PokkenConstants.BATTLE_RESULT.WIN else pokemon_data.c.win_vs_lan,
|
||||
battle_num_vs_lan=coalesce(pokemon_data.c.battle_num_vs_lan, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.LAN else coalesce(pokemon_data.c.battle_num_vs_lan, 0),
|
||||
win_vs_lan=coalesce(pokemon_data.c.win_vs_lan, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.LAN and match_result==PokkenConstants.BATTLE_RESULT.WIN else coalesce(pokemon_data.c.win_vs_lan, 0),
|
||||
|
||||
battle_num_vs_wan=pokemon_data.c.battle_num_vs_wan + 1 if match_type==PokkenConstants.BATTLE_TYPE.WAN else pokemon_data.c.battle_num_vs_wan,
|
||||
win_vs_wan=pokemon_data.c.win_vs_wan + 1 if match_type==PokkenConstants.BATTLE_TYPE.WAN and match_result==PokkenConstants.BATTLE_RESULT.WIN else pokemon_data.c.win_vs_wan,
|
||||
battle_num_vs_wan=coalesce(pokemon_data.c.battle_num_vs_wan, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.WAN else coalesce(pokemon_data.c.battle_num_vs_wan, 0),
|
||||
win_vs_wan=coalesce(pokemon_data.c.win_vs_wan, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.WAN and match_result==PokkenConstants.BATTLE_RESULT.WIN else coalesce(pokemon_data.c.win_vs_wan, 0),
|
||||
)
|
||||
|
||||
result = self.execute(sql)
|
||||
@ -335,11 +354,11 @@ class PokkenProfileData(BaseData):
|
||||
Records profile stats
|
||||
"""
|
||||
sql = update(profile).where(profile.c.user==user_id).values(
|
||||
ex_ko_num=profile.c.ex_ko_num + exkos,
|
||||
wko_num=profile.c.wko_num + wkos,
|
||||
timeup_win_num=profile.c.timeup_win_num + timeout_wins,
|
||||
cool_ko_num=profile.c.cool_ko_num + cool_kos,
|
||||
perfect_ko_num=profile.c.perfect_ko_num + perfects,
|
||||
ex_ko_num=coalesce(profile.c.ex_ko_num, 0) + exkos,
|
||||
wko_num=coalesce(profile.c.wko_num, 0) + wkos,
|
||||
timeup_win_num=coalesce(profile.c.timeup_win_num, 0) + timeout_wins,
|
||||
cool_ko_num=coalesce(profile.c.cool_ko_num, 0) + cool_kos,
|
||||
perfect_ko_num=coalesce(profile.c.perfect_ko_num, 0) + perfects,
|
||||
continue_num=continues,
|
||||
)
|
||||
|
||||
@ -348,10 +367,6 @@ class PokkenProfileData(BaseData):
|
||||
self.logger.warning(f"Failed to update stats for user {user_id}")
|
||||
|
||||
def update_support_team(self, user_id: int, support_id: int, support1: int = None, support2: int = None) -> None:
|
||||
if support1 == 4294967295:
|
||||
support1 = None
|
||||
if support2 == 4294967295:
|
||||
support2 = None
|
||||
sql = update(profile).where(profile.c.user==user_id).values(
|
||||
support_set_1_1=support1 if support_id == 1 else profile.c.support_set_1_1,
|
||||
support_set_1_2=support2 if support_id == 1 else profile.c.support_set_1_2,
|
||||
|
Loading…
Reference in New Issue
Block a user