3
2
forked from Dniel97/artemis

Adding SAO item table and adding party saving

This commit is contained in:
Midorica 2023-05-29 16:51:41 -04:00
parent 84cb786bde
commit d8af7be4a4
4 changed files with 299 additions and 70 deletions

View File

@ -73,7 +73,12 @@ class SaoBase:
user_id = -1
self.logger.error("Failed to register card!")
# Create profile with 3 basic heroes
profile_id = self.game_data.profile.create_profile(user_id)
self.game_data.item.put_hero_log(user_id, 101000010, 1, 0, 101000016, 0, 30086, 1001, 1002, 1003, 1005)
self.game_data.item.put_hero_log(user_id, 102000010, 1, 0, 103000006, 0, 30086, 1001, 1002, 1003, 1005)
self.game_data.item.put_hero_log(user_id, 103000010, 1, 0, 112000009, 0, 30086, 1001, 1002, 1003, 1005)
self.game_data.item.put_hero_party(user_id, 0, 101000010, 102000010, 103000010)
self.logger.info(f"User Authenticated: { access_code } | { user_id }")
@ -121,9 +126,19 @@ class SaoBase:
def handle_c600(self, request: Any) -> bytes:
#have_object/get_hero_log_user_data_list
heroIdsData = self.game_data.static.get_hero_ids(0, True)
req = bytes.fromhex(request)[24:]
req_struct = Struct(
Padding(16),
"user_id_size" / Rebuild(Int32ub, len_(this.user_id) * 2), # calculates the length of the user_id
"user_id" / PaddedString(this.user_id_size, "utf_16_le"), # user_id is a (zero) padded string
)
req_data = req_struct.parse(req)
user_id = req_data.user_id
hero_data = self.game_data.item.get_hero_logs(user_id)
resp = SaoGetHeroLogUserDataListResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1, heroIdsData)
resp = SaoGetHeroLogUserDataListResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1, hero_data)
return resp.make()
def handle_c602(self, request: Any) -> bytes:
@ -164,7 +179,23 @@ class SaoBase:
def handle_c804(self, request: Any) -> bytes:
#custom/get_party_data_list
resp = SaoGetPartyDataListResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1)
req = bytes.fromhex(request)[24:]
req_struct = Struct(
Padding(16),
"user_id_size" / Rebuild(Int32ub, len_(this.user_id) * 2), # calculates the length of the user_id
"user_id" / PaddedString(this.user_id_size, "utf_16_le"), # user_id is a (zero) padded string
)
req_data = req_struct.parse(req)
user_id = req_data.user_id
hero_party = self.game_data.item.get_hero_party(user_id, 0)
hero1_data = self.game_data.item.get_hero_log(user_id, hero_party[3])
hero2_data = self.game_data.item.get_hero_log(user_id, hero_party[4])
hero3_data = self.game_data.item.get_hero_log(user_id, hero_party[5])
resp = SaoGetPartyDataListResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1, hero1_data, hero2_data, hero3_data)
return resp.make()
def handle_c902(self, request: Any) -> bytes: # for whatever reason, having all entries empty or filled changes nothing
@ -197,7 +228,7 @@ class SaoBase:
#home/check_profile_card_used_reward
resp = SaoCheckProfileCardUsedRewardResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1)
return resp.make()
def handle_c806(self, request: Any) -> bytes:
#custom/change_party
req = bytes.fromhex(request)[24:]
@ -228,18 +259,40 @@ class SaoBase:
"sub_equipment_user_equipment_id_size" / Rebuild(Int32ub, len_(this.sub_equipment_user_equipment_id) * 2), # calculates the length of the sub_equipment_user_equipment_id
"sub_equipment_user_equipment_id" / PaddedString(this.sub_equipment_user_equipment_id_size, "utf_16_le"), # sub_equipment_user_equipment_id is a (zero) padded string
"skill_slot1_skill_id" / Int32ub, # skill_slot1_skill_id is a int,
"skill_slot2_skill_id" / Int32ub, # skill_slot2_skill_id is a int,
"skill_slot3_skill_id" / Int32ub, # skill_slot3_skill_id is a int,
"skill_slot4_skill_id" / Int32ub, # skill_slot4_skill_id is a int,
"skill_slot5_skill_id" / Int32ub, # skill_slot5_skill_id is a int,
"skill_slot2_skill_id" / Int32ub, # skill_slot1_skill_id is a int,
"skill_slot3_skill_id" / Int32ub, # skill_slot1_skill_id is a int,
"skill_slot4_skill_id" / Int32ub, # skill_slot1_skill_id is a int,
"skill_slot5_skill_id" / Int32ub, # skill_slot1_skill_id is a int,
)),
)),
)
req_data = req_struct.parse(req)
user_id = req_data.user_id
#self.logger.info(f"User Team Data: { req_data }")
for party_team in req_data.party_data_list[0].party_team_data_list:
hero_data = self.game_data.item.get_hero_log(user_id, party_team["user_hero_log_id"])
hero_level = 1
hero_exp = 0
if hero_data:
hero_level = hero_data["log_level"]
hero_exp = hero_data["log_exp"]
self.game_data.item.put_hero_log(
user_id,
party_team["user_hero_log_id"],
hero_level,
hero_exp,
party_team["main_weapon_user_equipment_id"],
party_team["sub_equipment_user_equipment_id"],
party_team["skill_slot1_skill_id"],
party_team["skill_slot2_skill_id"],
party_team["skill_slot3_skill_id"],
party_team["skill_slot4_skill_id"],
party_team["skill_slot5_skill_id"]
)
resp = SaoNoopResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1)
return resp.make()
@ -371,12 +424,6 @@ class SaoBase:
req_data = req_struct.parse(req)
#self.logger.info(f"User Get Col Data: { req_data.get_col }")
#self.logger.info(f"User Hero Log Exp Data: { req_data.get_hero_log_exp }")
#self.logger.info(f"User Score Data: { req_data.score_data[0] }")
#self.logger.info(f"User Discovery Enemy Data: { req_data.discovery_enemy_data_list }")
#self.logger.info(f"User Mission Data: { req_data.mission_data_list }")
resp = SaoEpisodePlayEndResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1)
return resp.make()

View File

@ -478,28 +478,49 @@ class SaoGetHeroLogUserDataListRequest(SaoBaseRequest):
super().__init__(data)
class SaoGetHeroLogUserDataListResponse(SaoBaseResponse):
def __init__(self, cmd, heroIdsData) -> None:
def __init__(self, cmd, hero_data) -> None:
super().__init__(cmd)
self.result = 1
#print(heroIdsData)
#print(list(map(str,heroIdsData)))
self.user_hero_log_id = []
self.log_level = []
self.max_log_level_extended_num = []
self.log_exp = []
self.last_set_skill_slot1_skill_id = []
self.last_set_skill_slot2_skill_id = []
self.last_set_skill_slot3_skill_id = []
self.last_set_skill_slot4_skill_id = []
self.last_set_skill_slot5_skill_id = []
for i in range(len(hero_data)):
self.user_hero_log_id.append(hero_data[i][2])
self.log_level.append(hero_data[i][3])
self.max_log_level_extended_num.append(hero_data[i][3])
self.log_exp.append(hero_data[i][4])
self.last_set_skill_slot1_skill_id.append(hero_data[i][7])
self.last_set_skill_slot2_skill_id.append(hero_data[i][8])
self.last_set_skill_slot3_skill_id.append(hero_data[i][9])
self.last_set_skill_slot4_skill_id.append(hero_data[i][10])
self.last_set_skill_slot5_skill_id.append(hero_data[i][11])
#print(self.user_hero_log_id)
#print(list(map(str,self.user_hero_log_id)))
# hero_log_user_data_list
self.user_hero_log_id = list(map(str,heroIdsData)) #str
self.hero_log_id = heroIdsData #int
self.log_level = 10 #short
self.max_log_level_extended_num = 10 #short
self.log_exp = 1000 #int
self.user_hero_log_id = list(map(str,self.user_hero_log_id)) #str
self.hero_log_id = list(map(int,self.user_hero_log_id)) #int
self.log_level = list(map(int,self.log_level)) #short
self.max_log_level_extended_num = list(map(int,self.log_level)) #short
self.log_exp = list(map(int,self.log_level)) #int
self.possible_awakening_flag = 0 #byte
self.awakening_stage = 0 #short
self.awakening_exp = 0 #int
self.skill_slot_correction_value = 0 #byte
self.last_set_skill_slot1_skill_id = 0 #short
self.last_set_skill_slot2_skill_id = 0 #short
self.last_set_skill_slot3_skill_id = 0 #short
self.last_set_skill_slot4_skill_id = 0 #short
self.last_set_skill_slot5_skill_id = 0 #short
self.last_set_skill_slot1_skill_id = list(map(int,self.last_set_skill_slot1_skill_id)) #short
self.last_set_skill_slot2_skill_id = list(map(int,self.last_set_skill_slot2_skill_id)) #short
self.last_set_skill_slot3_skill_id = list(map(int,self.last_set_skill_slot3_skill_id)) #short
self.last_set_skill_slot4_skill_id = list(map(int,self.last_set_skill_slot4_skill_id)) #short
self.last_set_skill_slot5_skill_id = list(map(int,self.last_set_skill_slot5_skill_id)) #short
self.property1_property_id = 0 #int
self.property1_value1 = 0 #int
self.property1_value2 = 0 #int
@ -573,18 +594,18 @@ class SaoGetHeroLogUserDataListResponse(SaoBaseResponse):
user_hero_log_id_size=len(self.user_hero_log_id[i]) * 2,
user_hero_log_id=[ord(x) for x in self.user_hero_log_id[i]],
hero_log_id=self.hero_log_id[i],
log_level=self.log_level,
max_log_level_extended_num=self.max_log_level_extended_num,
log_exp=self.log_exp,
log_level=self.log_level[i],
max_log_level_extended_num=self.max_log_level_extended_num[i],
log_exp=self.log_exp[i],
possible_awakening_flag=self.possible_awakening_flag,
awakening_stage=self.awakening_stage,
awakening_exp=self.awakening_exp,
skill_slot_correction_value=self.skill_slot_correction_value,
last_set_skill_slot1_skill_id=self.last_set_skill_slot1_skill_id,
last_set_skill_slot2_skill_id=self.last_set_skill_slot2_skill_id,
last_set_skill_slot3_skill_id=self.last_set_skill_slot3_skill_id,
last_set_skill_slot4_skill_id=self.last_set_skill_slot4_skill_id,
last_set_skill_slot5_skill_id=self.last_set_skill_slot5_skill_id,
last_set_skill_slot1_skill_id=self.last_set_skill_slot1_skill_id[i],
last_set_skill_slot2_skill_id=self.last_set_skill_slot2_skill_id[i],
last_set_skill_slot3_skill_id=self.last_set_skill_slot3_skill_id[i],
last_set_skill_slot4_skill_id=self.last_set_skill_slot4_skill_id[i],
last_set_skill_slot5_skill_id=self.last_set_skill_slot5_skill_id[i],
property1_property_id=self.property1_property_id,
property1_value1=self.property1_value1,
property1_value2=self.property1_value2,
@ -926,10 +947,10 @@ class SaoGetEpisodeAppendDataListResponse(SaoBaseResponse):
def make(self) -> bytes:
episode_data_struct = Struct(
"user_episode_append_id_size" / Int32ub, # big endian
"user_episode_append_id" / Int16ul[5], #forced to match the user_episode_append_id_list index which is always 5 chars for the episode ids
"user_id_size" / Int32ub, # big endian
"user_id" / Int16ul[6], # has to be exactly 6 chars in the user field... MANDATORY
"user_episode_append_id_size" / Rebuild(Int32ub, len_(this.user_episode_append_id) * 2), # calculates the length of the user_episode_append_id
"user_episode_append_id" / PaddedString(this.user_episode_append_id_size, "utf_16_le"), # user_episode_append_id is a (zero) padded string
"user_id_size" / Rebuild(Int32ub, len_(this.user_id) * 2), # calculates the length of the user_id
"user_id" / PaddedString(this.user_id_size, "utf_16_le"), # user_id is a (zero) padded string
"episode_append_id" / Int32ub,
"own_num" / Int32ub,
)
@ -955,10 +976,8 @@ class SaoGetEpisodeAppendDataListResponse(SaoBaseResponse):
for i in range(len(self.user_id_list)):
# add the episode_data_struct to the resp_struct.episode_append_data_list
resp_data.episode_append_data_list.append(dict(
user_episode_append_id_size=len(self.user_episode_append_id_list[i]) * 2,
user_episode_append_id=[ord(x) for x in self.user_episode_append_id_list[i]],
user_id_size=len(self.user_id_list[i]) * 2,
user_id=[ord(x) for x in self.user_id_list[i]],
user_episode_append_id=self.user_episode_append_id_list[i],
user_id=self.user_id_list[i],
episode_append_id=self.episode_append_id_list[i],
own_num=self.own_num_list[i],
))
@ -974,8 +993,9 @@ class SaoGetPartyDataListRequest(SaoBaseRequest):
super().__init__(data)
class SaoGetPartyDataListResponse(SaoBaseResponse): # Default party
def __init__(self, cmd) -> None:
def __init__(self, cmd, hero1_data, hero2_data, hero3_data) -> None:
super().__init__(cmd)
self.result = 1
self.party_data_list_size = 1 # Number of arrays
@ -985,36 +1005,36 @@ class SaoGetPartyDataListResponse(SaoBaseResponse): # Default party
self.user_party_team_id_1 = "0"
self.arrangement_num_1 = 0
self.user_hero_log_id_1 = "101000010"
self.main_weapon_user_equipment_id_1 = "101000016"
self.sub_equipment_user_equipment_id_1 = "0"
self.skill_slot1_skill_id_1 = 30086
self.skill_slot2_skill_id_1 = 1001
self.skill_slot3_skill_id_1 = 1002
self.skill_slot4_skill_id_1 = 1003
self.skill_slot5_skill_id_1 = 1005
self.user_hero_log_id_1 = str(hero1_data[2])
self.main_weapon_user_equipment_id_1 = str(hero1_data[5])
self.sub_equipment_user_equipment_id_1 = str(hero1_data[6])
self.skill_slot1_skill_id_1 = hero1_data[7]
self.skill_slot2_skill_id_1 = hero1_data[8]
self.skill_slot3_skill_id_1 = hero1_data[9]
self.skill_slot4_skill_id_1 = hero1_data[10]
self.skill_slot5_skill_id_1 = hero1_data[11]
self.user_party_team_id_2 = "0"
self.arrangement_num_2 = 0
self.user_hero_log_id_2 = "102000010"
self.main_weapon_user_equipment_id_2 = "103000006"
self.sub_equipment_user_equipment_id_2 = "0"
self.skill_slot1_skill_id_2 = 30086
self.skill_slot2_skill_id_2 = 1001
self.skill_slot3_skill_id_2 = 1002
self.skill_slot4_skill_id_2 = 1003
self.skill_slot5_skill_id_2 = 1005
self.user_hero_log_id_2 = str(hero2_data[2])
self.main_weapon_user_equipment_id_2 = str(hero2_data[5])
self.sub_equipment_user_equipment_id_2 = str(hero2_data[6])
self.skill_slot1_skill_id_2 = hero2_data[7]
self.skill_slot2_skill_id_2 = hero2_data[8]
self.skill_slot3_skill_id_2 = hero2_data[9]
self.skill_slot4_skill_id_2 = hero2_data[10]
self.skill_slot5_skill_id_2 = hero2_data[11]
self.user_party_team_id_3 = "0"
self.arrangement_num_3 = 0
self.user_hero_log_id_3 = "103000010"
self.main_weapon_user_equipment_id_3 = "112000009"
self.sub_equipment_user_equipment_id_3 = "0"
self.skill_slot1_skill_id_3 = 30086
self.skill_slot2_skill_id_3 = 1001
self.skill_slot3_skill_id_3 = 1002
self.skill_slot4_skill_id_3 = 1003
self.skill_slot5_skill_id_3 = 1005
self.user_hero_log_id_3 = str(hero3_data[2])
self.main_weapon_user_equipment_id_3 = str(hero3_data[5])
self.sub_equipment_user_equipment_id_3 = str(hero3_data[6])
self.skill_slot1_skill_id_3 = hero3_data[7]
self.skill_slot2_skill_id_3 = hero3_data[8]
self.skill_slot3_skill_id_3 = hero3_data[9]
self.skill_slot4_skill_id_3 = hero3_data[10]
self.skill_slot5_skill_id_3 = hero3_data[11]
def make(self) -> bytes:
# create a resp struct

View File

@ -1,2 +1,3 @@
from .profile import SaoProfileData
from .static import SaoStaticData
from .static import SaoStaticData
from .item import SaoItemData

161
titles/sao/schema/item.py Normal file
View File

@ -0,0 +1,161 @@
from typing import Optional, Dict, List
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_, case
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean
from sqlalchemy.schema import ForeignKey
from sqlalchemy.sql import func, select, update, delete
from sqlalchemy.engine import Row
from sqlalchemy.dialects.mysql import insert
from core.data.schema import BaseData, metadata
hero_log_data = Table(
"sao_hero_log_data",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("user_hero_log_id", Integer, nullable=False),
Column("log_level", Integer, nullable=False),
Column("log_exp", Integer, nullable=False),
Column("main_weapon", Integer, nullable=False),
Column("sub_equipment", Integer, nullable=False),
Column("skill_slot1_skill_id", Integer, nullable=False),
Column("skill_slot2_skill_id", Integer, nullable=False),
Column("skill_slot3_skill_id", Integer, nullable=False),
Column("skill_slot4_skill_id", Integer, nullable=False),
Column("skill_slot5_skill_id", Integer, nullable=False),
Column("get_date", TIMESTAMP, nullable=False, server_default=func.now()),
UniqueConstraint("user", "user_hero_log_id", name="sao_hero_log_data_uk"),
mysql_charset="utf8mb4",
)
hero_party = Table(
"sao_hero_party",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("user_party_team_id", Integer, nullable=False),
Column("user_hero_log_id_1", Integer, nullable=False),
Column("user_hero_log_id_2", Integer, nullable=False),
Column("user_hero_log_id_3", Integer, nullable=False),
UniqueConstraint("user", "user_party_team_id", name="sao_hero_party_uk"),
mysql_charset="utf8mb4",
)
class SaoItemData(BaseData):
def put_hero_log(self, user_id: int, user_hero_log_id: int, log_level: int, log_exp: int, main_weapon: int, sub_equipment: int, skill_slot1_skill_id: int, skill_slot2_skill_id: int, skill_slot3_skill_id: int, skill_slot4_skill_id: int, skill_slot5_skill_id: int) -> Optional[int]:
sql = insert(hero_log_data).values(
user=user_id,
user_hero_log_id=user_hero_log_id,
log_level=log_level,
log_exp=log_exp,
main_weapon=main_weapon,
sub_equipment=sub_equipment,
skill_slot1_skill_id=skill_slot1_skill_id,
skill_slot2_skill_id=skill_slot2_skill_id,
skill_slot3_skill_id=skill_slot3_skill_id,
skill_slot4_skill_id=skill_slot4_skill_id,
skill_slot5_skill_id=skill_slot5_skill_id,
)
conflict = sql.on_duplicate_key_update(
log_level=hero_log_data.c.log_level,
log_exp=hero_log_data.c.log_exp,
main_weapon=hero_log_data.c.main_weapon,
sub_equipment=hero_log_data.c.sub_equipment,
skill_slot1_skill_id=hero_log_data.c.skill_slot1_skill_id,
skill_slot2_skill_id=hero_log_data.c.skill_slot2_skill_id,
skill_slot3_skill_id=hero_log_data.c.skill_slot3_skill_id,
skill_slot4_skill_id=hero_log_data.c.skill_slot4_skill_id,
skill_slot5_skill_id=hero_log_data.c.skill_slot5_skill_id,
)
result = self.execute(conflict)
if result is None:
self.logger.error(
f"{__name__} failed to insert hero! user: {user_id}, user_hero_log_id: {user_hero_log_id}"
)
return None
return result.lastrowid
def put_hero_party(self, user_id: int, user_party_team_id: int, user_hero_log_id_1: int, user_hero_log_id_2: int, user_hero_log_id_3: int) -> Optional[int]:
sql = insert(hero_party).values(
user=user_id,
user_party_team_id=user_party_team_id,
user_hero_log_id_1=user_hero_log_id_1,
user_hero_log_id_2=user_hero_log_id_2,
user_hero_log_id_3=user_hero_log_id_3,
)
conflict = sql.on_duplicate_key_update(
user_hero_log_id_1=hero_party.c.user_hero_log_id_1,
user_hero_log_id_2=hero_party.c.user_hero_log_id_2,
user_hero_log_id_3=hero_party.c.user_hero_log_id_3,
)
result = self.execute(conflict)
if result is None:
self.logger.error(
f"{__name__} failed to insert hero party! user: {user_id}, user_party_team_id: {user_party_team_id}"
)
return None
return result.lastrowid
def get_hero_log(
self, user_id: int, user_hero_log_id: int = None
) -> Optional[List[Row]]:
"""
A catch-all hero lookup given a profile and user_party_team_id and ID specifiers
"""
sql = hero_log_data.select(
and_(
hero_log_data.c.user == user_id,
hero_log_data.c.user_hero_log_id == user_hero_log_id if user_hero_log_id is not None else True,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_hero_logs(
self, user_id: int
) -> Optional[List[Row]]:
"""
A catch-all hero lookup given a profile and user_party_team_id and ID specifiers
"""
sql = hero_log_data.select(
and_(
hero_log_data.c.user == user_id,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_hero_party(
self, user_id: int, user_party_team_id: int = None
) -> Optional[List[Row]]:
sql = hero_party.select(
and_(
hero_party.c.user == user_id,
hero_party.c.user_party_team_id == user_party_team_id if user_party_team_id is not None else True,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()