Equipments saving for SAO now completed

This commit is contained in:
Midorica 2023-05-30 18:03:52 -04:00
parent e466ddce55
commit bf6d126f8a
6 changed files with 271 additions and 22 deletions

View File

@ -30,6 +30,9 @@ Games listed below have been tested and confirmed working. Only game versions ol
+ POKKÉN TOURNAMENT + POKKÉN TOURNAMENT
+ Final Online + Final Online
+ Sword Art Online Arcade (partial support)
+ Final
## Requirements ## Requirements
- python 3 (tested working with 3.9 and 3.10, other versions YMMV) - python 3 (tested working with 3.9 and 3.10, other versions YMMV)
- pip - pip

View File

@ -81,6 +81,9 @@ class SaoBase:
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, 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_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.game_data.item.put_hero_party(user_id, 0, 101000010, 102000010, 103000010)
self.game_data.item.put_equipment_data(user_id, 101000016, 1, 200, 0, 0, 0)
self.game_data.item.put_equipment_data(user_id, 103000006, 1, 200, 0, 0, 0)
self.game_data.item.put_equipment_data(user_id, 112000009, 1, 200, 0, 0, 0)
self.logger.info(f"User Authenticated: { access_code } | { user_id }") self.logger.info(f"User Authenticated: { access_code } | { user_id }")
@ -145,9 +148,19 @@ class SaoBase:
def handle_c602(self, request: Any) -> bytes: def handle_c602(self, request: Any) -> bytes:
#have_object/get_equipment_user_data_list #have_object/get_equipment_user_data_list
equipmentIdsData = self.game_data.static.get_equipment_ids(0, True) req = bytes.fromhex(request)[24:]
req_struct = Struct(
resp = SaoGetEquipmentUserDataListResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1, equipmentIdsData) 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
equipment_data = self.game_data.item.get_user_equipments(user_id)
resp = SaoGetEquipmentUserDataListResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1, equipment_data)
return resp.make() return resp.make()
def handle_c604(self, request: Any) -> bytes: def handle_c604(self, request: Any) -> bytes:
@ -510,10 +523,11 @@ class SaoBase:
randomized_unanalyzed_id = choice(data_unanalyzed) randomized_unanalyzed_id = choice(data_unanalyzed)
heroList = self.game_data.static.get_hero_id(randomized_unanalyzed_id['CommonRewardId']) heroList = self.game_data.static.get_hero_id(randomized_unanalyzed_id['CommonRewardId'])
equipmentList = self.game_data.static.get_equipment_id(randomized_unanalyzed_id['CommonRewardId'])
if heroList: if heroList:
self.game_data.item.put_hero_log(req_data.user_id, randomized_unanalyzed_id['CommonRewardId'], 1, 0, 101000016, 0, 30086, 1001, 1002, 0, 0) self.game_data.item.put_hero_log(req_data.user_id, randomized_unanalyzed_id['CommonRewardId'], 1, 0, 101000016, 0, 30086, 1001, 1002, 0, 0)
if equipmentList:
# Item and Equipments saving will be done later here self.game_data.item.put_equipment_data(req_data.user_id, randomized_unanalyzed_id['CommonRewardId'], 1, 200, 0, 0, 0)
# Send response # Send response
@ -532,4 +546,4 @@ class SaoBase:
def handle_c90a(self, request: Any) -> bytes: #should be tweaked for proper item unlock def handle_c90a(self, request: Any) -> bytes: #should be tweaked for proper item unlock
#quest/episode_play_end_unanalyzed_log_fixed #quest/episode_play_end_unanalyzed_log_fixed
resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1) resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(int.from_bytes(bytes.fromhex(request[:4]), "big")+1)
return resp.make() return resp.make()

View File

@ -0,0 +1,121 @@
EquipmentLevelId,RequireExp
1,200,
2,400,
3,600,
4,800,
5,1000,
6,1200,
7,1400,
8,1600,
9,1800,
10,2000,
11,2200,
12,2400,
13,2600,
14,2800,
15,3000,
16,3200,
17,3400,
18,3600,
19,3800,
20,4000,
21,4200,
22,4400,
23,4600,
24,4800,
25,5000,
26,5200,
27,5400,
28,5600,
29,5800,
30,6000,
31,6200,
32,6400,
33,6600,
34,6800,
35,7000,
36,7200,
37,7400,
38,7600,
39,7800,
40,8000,
41,8200,
42,8400,
43,8600,
44,8800,
45,9000,
46,9200,
47,9400,
48,9600,
49,9800,
50,10000,
51,10200,
52,10400,
53,10600,
54,10800,
55,11000,
56,11200,
57,11400,
58,11600,
59,11800,
60,12000,
61,12200,
62,12400,
63,12600,
64,12800,
65,13000,
66,13200,
67,13400,
68,13600,
69,13800,
70,14000,
71,14200,
72,14400,
73,14600,
74,14800,
75,15000,
76,15200,
77,15400,
78,15600,
79,15800,
80,16000,
81,16200,
82,16400,
83,16600,
84,16800,
85,17000,
86,17200,
87,17400,
88,17600,
89,17800,
90,18000,
91,18200,
92,18400,
93,18600,
94,18800,
95,19000,
96,19200,
97,19400,
98,19600,
99,19800,
100,100000,
101,150000,
102,200000,
103,250000,
104,300000,
105,350000,
106,400000,
107,450000,
108,500000,
109,550000,
110,600000,
111,650000,
112,700000,
113,750000,
114,800000,
115,850000,
116,900000,
117,950000,
118,1000000,
119,1000000,
120,1000000,
1 EquipmentLevelId,RequireExp
2 1,200,
3 2,400,
4 3,600,
5 4,800,
6 5,1000,
7 6,1200,
8 7,1400,
9 8,1600,
10 9,1800,
11 10,2000,
12 11,2200,
13 12,2400,
14 13,2600,
15 14,2800,
16 15,3000,
17 16,3200,
18 17,3400,
19 18,3600,
20 19,3800,
21 20,4000,
22 21,4200,
23 22,4400,
24 23,4600,
25 24,4800,
26 25,5000,
27 26,5200,
28 27,5400,
29 28,5600,
30 29,5800,
31 30,6000,
32 31,6200,
33 32,6400,
34 33,6600,
35 34,6800,
36 35,7000,
37 36,7200,
38 37,7400,
39 38,7600,
40 39,7800,
41 40,8000,
42 41,8200,
43 42,8400,
44 43,8600,
45 44,8800,
46 45,9000,
47 46,9200,
48 47,9400,
49 48,9600,
50 49,9800,
51 50,10000,
52 51,10200,
53 52,10400,
54 53,10600,
55 54,10800,
56 55,11000,
57 56,11200,
58 57,11400,
59 58,11600,
60 59,11800,
61 60,12000,
62 61,12200,
63 62,12400,
64 63,12600,
65 64,12800,
66 65,13000,
67 66,13200,
68 67,13400,
69 68,13600,
70 69,13800,
71 70,14000,
72 71,14200,
73 72,14400,
74 73,14600,
75 74,14800,
76 75,15000,
77 76,15200,
78 77,15400,
79 78,15600,
80 79,15800,
81 80,16000,
82 81,16200,
83 82,16400,
84 83,16600,
85 84,16800,
86 85,17000,
87 86,17200,
88 87,17400,
89 88,17600,
90 89,17800,
91 90,18000,
92 91,18200,
93 92,18400,
94 93,18600,
95 94,18800,
96 95,19000,
97 96,19200,
98 97,19400,
99 98,19600,
100 99,19800,
101 100,100000,
102 101,150000,
103 102,200000,
104 103,250000,
105 104,300000,
106 105,350000,
107 106,400000,
108 107,450000,
109 108,500000,
110 109,550000,
111 110,600000,
112 111,650000,
113 112,700000,
114 113,750000,
115 114,800000,
116 115,850000,
117 116,900000,
118 117,950000,
119 118,1000000,
120 119,1000000,
121 120,1000000,

View File

@ -660,19 +660,57 @@ class SaoGetEquipmentUserDataListRequest(SaoBaseRequest):
super().__init__(data) super().__init__(data)
class SaoGetEquipmentUserDataListResponse(SaoBaseResponse): class SaoGetEquipmentUserDataListResponse(SaoBaseResponse):
def __init__(self, cmd, equipmentIdsData) -> None: def __init__(self, cmd, equipment_data) -> None:
super().__init__(cmd) super().__init__(cmd)
self.result = 1 self.result = 1
self.user_equipment_id = []
self.enhancement_value = []
self.max_enhancement_value_extended_num = []
self.enhancement_exp = []
self.awakening_stage = []
self.awakening_exp = []
self.possible_awakening_flag = []
equipment_level = 0
for i in range(len(equipment_data)):
# Calculate level based off experience and the CSV list
with open(r'titles/sao/data/EquipmentLevel.csv') as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
line_count = 0
data = []
rowf = False
for row in csv_reader:
if rowf==False:
rowf=True
else:
data.append(row)
exp = equipment_data[i][4]
for e in range(0,len(data)):
if exp>=int(data[e][1]) and exp<int(data[e+1][1]):
equipment_level = int(data[e][0])
break
self.user_equipment_id.append(equipment_data[i][2])
self.enhancement_value.append(equipment_level)
self.max_enhancement_value_extended_num.append(equipment_level)
self.enhancement_exp.append(equipment_data[i][4])
self.awakening_stage.append(equipment_data[i][5])
self.awakening_exp.append(equipment_data[i][6])
self.possible_awakening_flag.append(equipment_data[i][7])
# equipment_user_data_list # equipment_user_data_list
self.user_equipment_id = list(map(str,equipmentIdsData)) #str self.user_equipment_id = list(map(str,self.user_equipment_id)) #str
self.equipment_id = equipmentIdsData #int self.equipment_id = list(map(int,self.user_equipment_id)) #int
self.enhancement_value = 10 #short self.enhancement_value = list(map(int,self.enhancement_value)) #short
self.max_enhancement_value_extended_num = 10 #short self.max_enhancement_value_extended_num = list(map(int,self.max_enhancement_value_extended_num)) #short
self.enhancement_exp = 1000 #int self.enhancement_exp = list(map(int,self.enhancement_exp)) #int
self.possible_awakening_flag = 0 #byte self.possible_awakening_flag = list(map(int,self.possible_awakening_flag)) #byte
self.awakening_stage = 0 #short self.awakening_stage = list(map(int,self.awakening_stage)) #short
self.awakening_exp = 0 #int self.awakening_exp = list(map(int,self.awakening_exp)) #int
self.property1_property_id = 0 #int self.property1_property_id = 0 #int
self.property1_value1 = 0 #int self.property1_value1 = 0 #int
self.property1_value2 = 0 #int self.property1_value2 = 0 #int
@ -739,12 +777,12 @@ class SaoGetEquipmentUserDataListResponse(SaoBaseResponse):
user_equipment_id_size=len(self.user_equipment_id[i]) * 2, user_equipment_id_size=len(self.user_equipment_id[i]) * 2,
user_equipment_id=[ord(x) for x in self.user_equipment_id[i]], user_equipment_id=[ord(x) for x in self.user_equipment_id[i]],
equipment_id=self.equipment_id[i], equipment_id=self.equipment_id[i],
enhancement_value=self.enhancement_value, enhancement_value=self.enhancement_value[i],
max_enhancement_value_extended_num=self.max_enhancement_value_extended_num, max_enhancement_value_extended_num=self.max_enhancement_value_extended_num[i],
enhancement_exp=self.enhancement_exp, enhancement_exp=self.enhancement_exp[i],
possible_awakening_flag=self.possible_awakening_flag, possible_awakening_flag=self.possible_awakening_flag[i],
awakening_stage=self.awakening_stage, awakening_stage=self.awakening_stage[i],
awakening_exp=self.awakening_exp, awakening_exp=self.awakening_exp[i],
property1_property_id=self.property1_property_id, property1_property_id=self.property1_property_id,
property1_value1=self.property1_value1, property1_value1=self.property1_value1,
property1_value2=self.property1_value2, property1_value2=self.property1_value2,

View File

@ -8,6 +8,26 @@ from sqlalchemy.dialects.mysql import insert
from core.data.schema import BaseData, metadata from core.data.schema import BaseData, metadata
equipment_data = Table(
"sao_equipment_data",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("equipment_id", Integer, nullable=False),
Column("enhancement_value", Integer, nullable=False),
Column("enhancement_exp", Integer, nullable=False),
Column("awakening_exp", Integer, nullable=False),
Column("awakening_stage", Integer, nullable=False),
Column("possible_awakening_flag", Integer, nullable=False),
Column("get_date", TIMESTAMP, nullable=False, server_default=func.now()),
UniqueConstraint("user", "equipment_id", name="sao_equipment_data_uk"),
mysql_charset="utf8mb4",
)
hero_log_data = Table( hero_log_data = Table(
"sao_hero_log_data", "sao_hero_log_data",
metadata, metadata,
@ -84,6 +104,34 @@ class SaoItemData(BaseData):
self.logger.error(f"Failed to create SAO session for user {user_id}!") self.logger.error(f"Failed to create SAO session for user {user_id}!")
return None return None
return result.lastrowid return result.lastrowid
def put_equipment_data(self, user_id: int, equipment_id: int, enhancement_value: int, enhancement_exp: int, awakening_exp: int, awakening_stage: int, possible_awakening_flag: int) -> Optional[int]:
sql = insert(equipment_data).values(
user=user_id,
equipment_id=equipment_id,
enhancement_value=enhancement_value,
enhancement_exp=enhancement_exp,
awakening_exp=awakening_exp,
awakening_stage=awakening_stage,
possible_awakening_flag=possible_awakening_flag,
)
conflict = sql.on_duplicate_key_update(
enhancement_value=enhancement_value,
enhancement_exp=enhancement_exp,
awakening_exp=awakening_exp,
awakening_stage=awakening_stage,
possible_awakening_flag=possible_awakening_flag,
)
result = self.execute(conflict)
if result is None:
self.logger.error(
f"{__name__} failed to insert equipment! user: {user_id}, equipment_id: {equipment_id}"
)
return None
return result.lastrowid
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]: 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( sql = insert(hero_log_data).values(
@ -144,6 +192,23 @@ class SaoItemData(BaseData):
return None return None
return result.lastrowid return result.lastrowid
def get_user_equipments(
self, user_id: int
) -> Optional[List[Row]]:
"""
A catch-all equipments lookup given a profile
"""
sql = equipment_data.select(
and_(
equipment_data.c.user == user_id,
)
)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def get_hero_log( def get_hero_log(
self, user_id: int, user_hero_log_id: int = None self, user_id: int, user_hero_log_id: int = None
@ -167,7 +232,7 @@ class SaoItemData(BaseData):
self, user_id: int self, user_id: int
) -> Optional[List[Row]]: ) -> Optional[List[Row]]:
""" """
A catch-all hero lookup given a profile and user_party_team_id and ID specifiers A catch-all hero lookup given a profile
""" """
sql = hero_log_data.select( sql = hero_log_data.select(
and_( and_(

View File

@ -264,6 +264,14 @@ class SaoStaticData(BaseData):
return None return None
return [list[2] for list in result.fetchall()] return [list[2] for list in result.fetchall()]
def get_equipment_id(self, equipmentId: int) -> Optional[Dict]:
sql = equipment.select(equipment.c.equipmentId == equipmentId)
result = self.execute(sql)
if result is None:
return None
return result.fetchone()
def get_equipment_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: def get_equipment_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]:
sql = equipment.select(equipment.c.version == version and equipment.c.enabled == enabled).order_by( sql = equipment.select(equipment.c.version == version and equipment.c.enabled == enabled).order_by(
equipment.c.equipmentId.asc() equipment.c.equipmentId.asc()