fixing unanalyzed reward request for SAO

This commit is contained in:
Midorica 2023-07-02 14:25:24 -04:00
parent 20389011e9
commit 84e880e94f
5 changed files with 17400 additions and 17330 deletions

View File

@ -815,6 +815,8 @@ class SaoBase:
) )
# Grab the rare loot from the table, match it with the right item and then push to the player profile # Grab the rare loot from the table, match it with the right item and then push to the player profile
json_data = {"data": []}
for r in range(0,req_data.get_rare_drop_data_list_length): for r in range(0,req_data.get_rare_drop_data_list_length):
rewardList = self.game_data.static.get_rare_drop_id(int(req_data.get_rare_drop_data_list[r].quest_rare_drop_id)) rewardList = self.game_data.static.get_rare_drop_id(int(req_data.get_rare_drop_data_list[r].quest_rare_drop_id))
commonRewardId = rewardList["commonRewardId"] commonRewardId = rewardList["commonRewardId"]
@ -851,8 +853,12 @@ class SaoBase:
if itemList: if itemList:
self.game_data.item.put_item(user_id, randomized_unanalyzed_id['CommonRewardId']) self.game_data.item.put_item(user_id, randomized_unanalyzed_id['CommonRewardId'])
json_data["data"].append(randomized_unanalyzed_id['CommonRewardId'])
# Send response # Send response
self.game_data.item.create_end_session(user_id, episode_id, quest_clear_flag, json_data["data"])
resp = SaoEpisodePlayEndResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1) resp = SaoEpisodePlayEndResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1)
return resp.make() return resp.make()
@ -1118,6 +1124,8 @@ class SaoBase:
hero_data["skill_slot5_skill_id"] hero_data["skill_slot5_skill_id"]
) )
json_data = {"data": []}
# Grab the rare loot from the table, match it with the right item and then push to the player profile # Grab the rare loot from the table, match it with the right item and then push to the player profile
for r in range(0,req_data.get_rare_drop_data_list_length): for r in range(0,req_data.get_rare_drop_data_list_length):
rewardList = self.game_data.static.get_rare_drop_id(int(req_data.get_rare_drop_data_list[r].quest_rare_drop_id)) rewardList = self.game_data.static.get_rare_drop_id(int(req_data.get_rare_drop_data_list[r].quest_rare_drop_id))
@ -1155,8 +1163,12 @@ class SaoBase:
if itemList: if itemList:
self.game_data.item.put_item(user_id, randomized_unanalyzed_id['CommonRewardId']) self.game_data.item.put_item(user_id, randomized_unanalyzed_id['CommonRewardId'])
json_data["data"].append(randomized_unanalyzed_id['CommonRewardId'])
# Send response # Send response
self.game_data.item.create_end_session(user_id, trial_tower_id, quest_clear_flag, json_data["data"])
resp = SaoTrialTowerPlayEndResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1) resp = SaoTrialTowerPlayEndResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1)
return resp.make() return resp.make()
@ -1176,32 +1188,9 @@ class SaoBase:
req_data = req_struct.parse(req) req_data = req_struct.parse(req)
user_id = req_data.user_id user_id = req_data.user_id
with open('titles/sao/data/RewardTable.csv', 'r') as f: end_session_data = self.game_data.item.get_end_session(user_id)
keys_unanalyzed = next(f).strip().split(',')
data_unanalyzed = list(DictReader(f, fieldnames=keys_unanalyzed))
randomized_unanalyzed_id = choice(data_unanalyzed) resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1, end_session_data[4])
heroList = self.game_data.static.get_hero_id(randomized_unanalyzed_id['CommonRewardId'])
i = 0
# Create a loop to check if the id is a hero or else try 15 times before closing the loop and sending a dummy hero
while not heroList:
if i == 15:
# Return the dummy hero but not save it
resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1, 102000070)
return resp.make()
i += 1
randomized_unanalyzed_id = choice(data_unanalyzed)
heroList = self.game_data.static.get_hero_id(randomized_unanalyzed_id['CommonRewardId'])
hero_data = self.game_data.item.get_hero_log(user_id, randomized_unanalyzed_id['CommonRewardId'])
# Avoid having a duplicated card and cause an overwrite
if not hero_data:
self.game_data.item.put_hero_log(user_id, randomized_unanalyzed_id['CommonRewardId'], 1, 0, 101000016, 0, 30086, 1001, 1002, 0, 0)
resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1, randomized_unanalyzed_id['CommonRewardId'])
return resp.make() return resp.make()
def handle_c91a(self, request: Any) -> bytes: # handler is identical to the episode def handle_c91a(self, request: Any) -> bytes: # handler is identical to the episode

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@ from datetime import datetime
from construct import * from construct import *
import sys import sys
import csv import csv
from csv import *
class SaoBaseRequest: class SaoBaseRequest:
def __init__(self, data: bytes) -> None: def __init__(self, data: bytes) -> None:
@ -1661,44 +1662,78 @@ class SaoEpisodePlayEndUnanalyzedLogFixedRequest(SaoBaseRequest):
super().__init__(data) super().__init__(data)
class SaoEpisodePlayEndUnanalyzedLogFixedResponse(SaoBaseResponse): class SaoEpisodePlayEndUnanalyzedLogFixedResponse(SaoBaseResponse):
def __init__(self, cmd, randomized_unanalyzed_id) -> None: def __init__(self, cmd, end_session_data) -> None:
super().__init__(cmd) super().__init__(cmd)
self.result = 1 self.result = 1
self.play_end_unanalyzed_log_reward_data_list_size = 1 # Number of arrays
self.unanalyzed_log_grade_id = 3 # RewardTable.csv self.unanalyzed_log_grade_id = []
self.common_reward_data_size = 1
self.common_reward_type_1 = 1 self.common_reward_type = []
self.common_reward_id_1 = int(randomized_unanalyzed_id) self.common_reward_id = []
self.common_reward_num_1 = 1 self.common_reward_num = 1
for x in range(len(end_session_data)):
self.common_reward_id.append(end_session_data[x])
with open('titles/sao/data/RewardTable.csv', 'r') as f:
keys_unanalyzed = next(f).strip().split(',')
data_unanalyzed = list(DictReader(f, fieldnames=keys_unanalyzed))
for i in range(len(data_unanalyzed)):
if int(data_unanalyzed[i]["CommonRewardId"]) == int(end_session_data[x]):
self.unanalyzed_log_grade_id.append(int(data_unanalyzed[i]["UnanalyzedLogGradeId"]))
self.common_reward_type.append(int(data_unanalyzed[i]["CommonRewardType"]))
break
self.unanalyzed_log_grade_id = list(map(int,self.unanalyzed_log_grade_id)) #int
self.common_reward_type = list(map(int,self.common_reward_type)) #int
self.common_reward_id = list(map(int,self.common_reward_id)) #int
def make(self) -> bytes: def make(self) -> bytes:
#new stuff
common_reward_data_struct = Struct(
"common_reward_type" / Int16ub,
"common_reward_id" / Int32ub,
"common_reward_num" / Int32ub,
)
play_end_unanalyzed_log_reward_data_list_struct = Struct(
"unanalyzed_log_grade_id" / Int32ub,
"common_reward_data_size" / Rebuild(Int32ub, len_(this.common_reward_data)), # big endian
"common_reward_data" / Array(this.common_reward_data_size, common_reward_data_struct),
)
# create a resp struct # create a resp struct
resp_struct = Struct( resp_struct = Struct(
"result" / Int8ul, # result is either 0 or 1 "result" / Int8ul, # result is either 0 or 1
"play_end_unanalyzed_log_reward_data_list_size" / Int32ub, # big endian "play_end_unanalyzed_log_reward_data_list_size" / Rebuild(Int32ub, len_(this.play_end_unanalyzed_log_reward_data_list)), # big endian
"play_end_unanalyzed_log_reward_data_list" / Array(this.play_end_unanalyzed_log_reward_data_list_size, play_end_unanalyzed_log_reward_data_list_struct),
"unanalyzed_log_grade_id" / Int32ub,
"common_reward_data_size" / Int32ub,
"common_reward_type_1" / Int16ub,
"common_reward_id_1" / Int32ub,
"common_reward_num_1" / Int32ub,
) )
resp_data = resp_struct.build(dict( resp_data = resp_struct.parse(resp_struct.build(dict(
result=self.result, result=self.result,
play_end_unanalyzed_log_reward_data_list_size=self.play_end_unanalyzed_log_reward_data_list_size, play_end_unanalyzed_log_reward_data_list_size=0,
play_end_unanalyzed_log_reward_data_list=[],
)))
unanalyzed_log_grade_id=self.unanalyzed_log_grade_id, for i in range(len(self.common_reward_id)):
common_reward_data_size=self.common_reward_data_size, reward_resp_data = dict(
unanalyzed_log_grade_id=self.unanalyzed_log_grade_id[i],
common_reward_data_size=0,
common_reward_data=[],
)
common_reward_type_1=self.common_reward_type_1, reward_resp_data["common_reward_data"].append(dict(
common_reward_id_1=self.common_reward_id_1, common_reward_type=self.common_reward_type[i],
common_reward_num_1=self.common_reward_num_1, common_reward_id=self.common_reward_id[i],
common_reward_num=self.common_reward_num,
)) ))
resp_data.play_end_unanalyzed_log_reward_data_list.append(reward_resp_data)
# finally, rebuild the resp_data
resp_data = resp_struct.build(resp_data)
self.length = len(resp_data) self.length = len(resp_data)
return super().make() + resp_data return super().make() + resp_data
@ -1733,8 +1768,8 @@ class SaoGetQuestSceneUserDataListResponse(SaoBaseResponse):
self.concurrent_destroying_num.append(quest_data[i][7]) self.concurrent_destroying_num.append(quest_data[i][7])
# quest_scene_ex_bonus_user_data_list # quest_scene_ex_bonus_user_data_list
self.achievement_flag = [[1, 1, 1],[1, 1, 1]] self.achievement_flag = [1,1,1]
self.ex_bonus_table_id = [[1, 2, 3],[4, 5, 6]] self.ex_bonus_table_id = [1,2,3]
self.quest_type = list(map(int,self.quest_type)) #int self.quest_type = list(map(int,self.quest_type)) #int
@ -1748,8 +1783,8 @@ class SaoGetQuestSceneUserDataListResponse(SaoBaseResponse):
def make(self) -> bytes: def make(self) -> bytes:
#new stuff #new stuff
quest_scene_ex_bonus_user_data_list_struct = Struct( quest_scene_ex_bonus_user_data_list_struct = Struct(
"achievement_flag" / Int32ub, # big endian
"ex_bonus_table_id" / Int32ub, # big endian "ex_bonus_table_id" / Int32ub, # big endian
"achievement_flag" / Int8ul, # result is either 0 or 1
) )
quest_scene_best_score_user_data_struct = Struct( quest_scene_best_score_user_data_struct = Struct(
@ -1803,7 +1838,6 @@ class SaoGetQuestSceneUserDataListResponse(SaoBaseResponse):
concurrent_destroying_num=self.concurrent_destroying_num[i], concurrent_destroying_num=self.concurrent_destroying_num[i],
)) ))
resp_data.quest_scene_user_data_list.append(quest_resp_data) resp_data.quest_scene_user_data_list.append(quest_resp_data)
# finally, rebuild the resp_data # finally, rebuild the resp_data

View File

@ -112,5 +112,4 @@ class SaoServlet(resource.Resource):
self.logger.info(f"Handler {req_url} - {sao_request[:4]} request") self.logger.info(f"Handler {req_url} - {sao_request[:4]} request")
self.logger.debug(f"Request: {request.content.getvalue().hex()}") self.logger.debug(f"Request: {request.content.getvalue().hex()}")
self.logger.debug(f"Response: {handler(sao_request).hex()}")
return handler(sao_request) return handler(sao_request)

View File

@ -1,6 +1,6 @@
from typing import Optional, Dict, List from typing import Optional, Dict, List
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_, case from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_, case
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON
from sqlalchemy.schema import ForeignKey from sqlalchemy.schema import ForeignKey
from sqlalchemy.sql import func, select, update, delete from sqlalchemy.sql import func, select, update, delete
from sqlalchemy.engine import Row from sqlalchemy.engine import Row
@ -122,6 +122,22 @@ sessions = Table(
mysql_charset="utf8mb4", mysql_charset="utf8mb4",
) )
end_sessions = Table(
"sao_end_sessions",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("quest_id", Integer, nullable=False),
Column("play_result_flag", Boolean, nullable=False),
Column("reward_data", JSON, nullable=True),
Column("play_date", TIMESTAMP, nullable=False, server_default=func.now()),
mysql_charset="utf8mb4",
)
class SaoItemData(BaseData): class SaoItemData(BaseData):
def create_session(self, user_id: int, user_party_team_id: int, episode_id: int, play_mode: int, quest_drop_boost_apply_flag: int) -> Optional[int]: def create_session(self, user_id: int, user_party_team_id: int, episode_id: int, play_mode: int, quest_drop_boost_apply_flag: int) -> Optional[int]:
sql = insert(sessions).values( sql = insert(sessions).values(
@ -140,6 +156,22 @@ class SaoItemData(BaseData):
return None return None
return result.lastrowid return result.lastrowid
def create_end_session(self, user_id: int, quest_id: int, play_result_flag: bool, reward_data: JSON) -> Optional[int]:
sql = insert(end_sessions).values(
user=user_id,
quest_id=quest_id,
play_result_flag=play_result_flag,
reward_data=reward_data,
)
conflict = sql.on_duplicate_key_update(user=user_id)
result = self.execute(conflict)
if result is None:
self.logger.error(f"Failed to create SAO end session for user {user_id}!")
return None
return result.lastrowid
def put_item(self, user_id: int, item_id: int) -> Optional[int]: def put_item(self, user_id: int, item_id: int) -> Optional[int]:
sql = insert(item_data).values( sql = insert(item_data).values(
user=user_id, user=user_id,
@ -418,6 +450,22 @@ class SaoItemData(BaseData):
return None return None
return result.fetchone() return result.fetchone()
def get_end_session(
self, user_id: int = None
) -> Optional[List[Row]]:
sql = end_sessions.select(
and_(
end_sessions.c.user == user_id,
)
).order_by(
end_sessions.c.play_date.asc()
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def remove_hero_log(self, user_id: int, user_hero_log_id: int) -> None: def remove_hero_log(self, user_id: int, user_hero_log_id: int) -> None:
sql = hero_log_data.delete( sql = hero_log_data.delete(
and_( and_(