Skip to content

Commit ec4bc69

Browse files
JaredWeakStrikeqwint
authored andcommitted
KH2: Client Optimizations and some QoL (ArchipelagoMW#4547)
* adding qwints suggestions * add stat increase protection and ingame yml stuff * idk how I forgot these * reword things * Update worlds/kh2/Client.py Co-authored-by: qwint <qwint.42@gmail.com> * 3.12 compat * too long of a line * why didnt I do this before lol * reading is hard * missed one * forgot the self * fix crash if you get datapackage that isnt kh2 * update to main? * update to use 0.10 as base and fix violet's base 0 on hex values * reverting this because I'm bad at my job --------- Co-authored-by: qwint <qwint.42@gmail.com>
1 parent 456adf8 commit ec4bc69

File tree

2 files changed

+140
-73
lines changed

2 files changed

+140
-73
lines changed

worlds/kh2/Client.py

Lines changed: 79 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import ModuleUpdate
2+
import Utils
23

34
ModuleUpdate.update()
45

@@ -23,6 +24,7 @@ class KH2Context(CommonContext):
2324

2425
def __init__(self, server_address, password):
2526
super(KH2Context, self).__init__(server_address, password)
27+
2628
self.goofy_ability_to_slot = dict()
2729
self.donald_ability_to_slot = dict()
2830
self.all_weapon_location_id = None
@@ -35,6 +37,7 @@ def __init__(self, server_address, password):
3537
self.serverconneced = False
3638
self.item_name_to_data = {name: data for name, data, in item_dictionary_table.items()}
3739
self.location_name_to_data = {name: data for name, data, in all_locations.items()}
40+
self.kh2_data_package = {}
3841
self.kh2_loc_name_to_id = None
3942
self.kh2_item_name_to_id = None
4043
self.lookup_id_to_item = None
@@ -83,6 +86,8 @@ def __init__(self, server_address, password):
8386
},
8487
}
8588
self.kh2seedname = None
89+
self.kh2_seed_save_path_join = None
90+
8691
self.kh2slotdata = None
8792
self.mem_json = None
8893
self.itemamount = {}
@@ -114,26 +119,18 @@ def __init__(self, server_address, password):
114119
# 255: {}, # starting screen
115120
}
116121
self.last_world_int = -1
117-
# 0x2A09C00+0x40 is the sve anchor. +1 is the last saved room
118-
# self.sveroom = 0x2A09C00 + 0x41
119-
# 0 not in battle 1 in yellow battle 2 red battle #short
120-
# self.inBattle = 0x2A0EAC4 + 0x40
121-
# self.onDeath = 0xAB9078
122122
# PC Address anchors
123-
# self.Now = 0x0714DB8 old address
124-
# epic addresses
123+
# epic .10 addresses
125124
self.Now = 0x0716DF8
126-
self.Save = 0x09A92F0
125+
self.Save = 0x9A9330
127126
self.Journal = 0x743260
128127
self.Shop = 0x743350
129-
self.Slot1 = 0x2A22FD8
130-
# self.Sys3 = 0x2A59DF0
131-
# self.Bt10 = 0x2A74880
132-
# self.BtlEnd = 0x2A0D3E0
133-
# self.Slot1 = 0x2A20C98 old address
128+
self.Slot1 = 0x2A23018
134129

135130
self.kh2_game_version = None # can be egs or steam
136131

132+
self.kh2_seed_save_path = None
133+
137134
self.chest_set = set(exclusion_table["Chests"])
138135
self.keyblade_set = set(CheckDupingItems["Weapons"]["Keyblades"])
139136
self.staff_set = set(CheckDupingItems["Weapons"]["Staffs"])
@@ -194,17 +191,15 @@ async def connection_closed(self):
194191
self.kh2connected = False
195192
self.serverconneced = False
196193
if self.kh2seedname is not None and self.auth is not None:
197-
with open(os.path.join(self.game_communication_path, f"kh2save2{self.kh2seedname}{self.auth}.json"),
198-
'w') as f:
194+
with open(self.kh2_seed_save_path_join, 'w') as f:
199195
f.write(json.dumps(self.kh2_seed_save, indent=4))
200196
await super(KH2Context, self).connection_closed()
201197

202198
async def disconnect(self, allow_autoreconnect: bool = False):
203199
self.kh2connected = False
204200
self.serverconneced = False
205201
if self.kh2seedname not in {None} and self.auth not in {None}:
206-
with open(os.path.join(self.game_communication_path, f"kh2save2{self.kh2seedname}{self.auth}.json"),
207-
'w') as f:
202+
with open(self.kh2_seed_save_path_join, 'w') as f:
208203
f.write(json.dumps(self.kh2_seed_save, indent=4))
209204
await super(KH2Context, self).disconnect()
210205

@@ -217,8 +212,7 @@ def endpoints(self):
217212

218213
async def shutdown(self):
219214
if self.kh2seedname not in {None} and self.auth not in {None}:
220-
with open(os.path.join(self.game_communication_path, f"kh2save2{self.kh2seedname}{self.auth}.json"),
221-
'w') as f:
215+
with open(self.kh2_seed_save_path_join, 'w') as f:
222216
f.write(json.dumps(self.kh2_seed_save, indent=4))
223217
await super(KH2Context, self).shutdown()
224218

@@ -232,7 +226,7 @@ def kh2_write_byte(self, address, value):
232226
return self.kh2.write_bytes(self.kh2.base_address + address, value.to_bytes(1, 'big'), 1)
233227

234228
def kh2_read_byte(self, address):
235-
return int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + address, 1), "big")
229+
return int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + address, 1))
236230

237231
def kh2_read_int(self, address):
238232
return self.kh2.read_int(self.kh2.base_address + address)
@@ -244,11 +238,14 @@ def kh2_read_string(self, address, length):
244238
return self.kh2.read_string(self.kh2.base_address + address, length)
245239

246240
def on_package(self, cmd: str, args: dict):
247-
if cmd in {"RoomInfo"}:
241+
if cmd == "RoomInfo":
248242
self.kh2seedname = args['seed_name']
243+
self.kh2_seed_save_path = f"kh2save2{self.kh2seedname}{self.auth}.json"
244+
self.kh2_seed_save_path_join = os.path.join(self.game_communication_path, self.kh2_seed_save_path)
245+
249246
if not os.path.exists(self.game_communication_path):
250247
os.makedirs(self.game_communication_path)
251-
if not os.path.exists(self.game_communication_path + f"\kh2save2{self.kh2seedname}{self.auth}.json"):
248+
if not os.path.exists(self.kh2_seed_save_path_join):
252249
self.kh2_seed_save = {
253250
"Levels": {
254251
"SoraLevel": 0,
@@ -261,12 +258,11 @@ def on_package(self, cmd: str, args: dict):
261258
},
262259
"SoldEquipment": [],
263260
}
264-
with open(os.path.join(self.game_communication_path, f"kh2save2{self.kh2seedname}{self.auth}.json"),
265-
'wt') as f:
261+
with open(self.kh2_seed_save_path_join, 'wt') as f:
266262
pass
267263
# self.locations_checked = set()
268-
elif os.path.exists(self.game_communication_path + f"\kh2save2{self.kh2seedname}{self.auth}.json"):
269-
with open(self.game_communication_path + f"\kh2save2{self.kh2seedname}{self.auth}.json", 'r') as f:
264+
elif os.path.exists(self.kh2_seed_save_path_join):
265+
with open(self.kh2_seed_save_path_join) as f:
270266
self.kh2_seed_save = json.load(f)
271267
if self.kh2_seed_save is None:
272268
self.kh2_seed_save = {
@@ -284,13 +280,22 @@ def on_package(self, cmd: str, args: dict):
284280
# self.locations_checked = set(self.kh2_seed_save_cache["LocationsChecked"])
285281
# self.serverconneced = True
286282

287-
if cmd in {"Connected"}:
288-
asyncio.create_task(self.send_msgs([{"cmd": "GetDataPackage", "games": ["Kingdom Hearts 2"]}]))
283+
if cmd == "Connected":
289284
self.kh2slotdata = args['slot_data']
290-
# self.kh2_local_items = {int(location): item for location, item in self.kh2slotdata["LocalItems"].items()}
285+
286+
self.kh2_data_package = Utils.load_data_package_for_checksum(
287+
"Kingdom Hearts 2", self.checksums["Kingdom Hearts 2"])
288+
289+
if "location_name_to_id" in self.kh2_data_package:
290+
self.data_package_kh2_cache(
291+
self.kh2_data_package["location_name_to_id"], self.kh2_data_package["item_name_to_id"])
292+
self.connect_to_game()
293+
else:
294+
asyncio.create_task(self.send_msgs([{"cmd": "GetDataPackage", "games": ["Kingdom Hearts 2"]}]))
295+
291296
self.locations_checked = set(args["checked_locations"])
292297

293-
if cmd in {"ReceivedItems"}:
298+
if cmd == "ReceivedItems":
294299
# 0x2546
295300
# 0x2658
296301
# 0x276A
@@ -338,42 +343,44 @@ def on_package(self, cmd: str, args: dict):
338343
for item in args['items']:
339344
asyncio.create_task(self.give_item(item.item, item.location))
340345

341-
if cmd in {"RoomUpdate"}:
346+
if cmd == "RoomUpdate":
342347
if "checked_locations" in args:
343348
new_locations = set(args["checked_locations"])
344349
self.locations_checked |= new_locations
345350

346-
if cmd in {"DataPackage"}:
351+
if cmd == "DataPackage":
347352
if "Kingdom Hearts 2" in args["data"]["games"]:
348-
self.data_package_kh2_cache(args)
349-
if "KeybladeAbilities" in self.kh2slotdata.keys():
350-
# sora ability to slot
351-
self.AbilityQuantityDict.update(self.kh2slotdata["KeybladeAbilities"])
352-
# itemid:[slots that are available for that item]
353-
self.AbilityQuantityDict.update(self.kh2slotdata["StaffAbilities"])
354-
self.AbilityQuantityDict.update(self.kh2slotdata["ShieldAbilities"])
355-
356-
all_weapon_location_id = []
357-
for weapon_location in all_weapon_slot:
358-
all_weapon_location_id.append(self.kh2_loc_name_to_id[weapon_location])
359-
self.all_weapon_location_id = set(all_weapon_location_id)
360-
361-
try:
362-
if not self.kh2:
363-
self.kh2 = pymem.Pymem(process_name="KINGDOM HEARTS II FINAL MIX")
364-
self.get_addresses()
365-
366-
except Exception as e:
367-
if self.kh2connected:
368-
self.kh2connected = False
369-
logger.info("Game is not open.")
370-
self.serverconneced = True
371-
asyncio.create_task(self.send_msgs([{'cmd': 'Sync'}]))
372-
373-
def data_package_kh2_cache(self, args):
374-
self.kh2_loc_name_to_id = args["data"]["games"]["Kingdom Hearts 2"]["location_name_to_id"]
353+
self.data_package_kh2_cache(
354+
args["data"]["games"]["Kingdom Hearts 2"]["location_name_to_id"],
355+
args["data"]["games"]["Kingdom Hearts 2"]["item_name_to_id"])
356+
self.connect_to_game()
357+
asyncio.create_task(self.send_msgs([{'cmd': 'Sync'}]))
358+
359+
def connect_to_game(self):
360+
if "KeybladeAbilities" in self.kh2slotdata.keys():
361+
# sora ability to slot
362+
self.AbilityQuantityDict.update(self.kh2slotdata["KeybladeAbilities"])
363+
# itemid:[slots that are available for that item]
364+
self.AbilityQuantityDict.update(self.kh2slotdata["StaffAbilities"])
365+
self.AbilityQuantityDict.update(self.kh2slotdata["ShieldAbilities"])
366+
367+
self.all_weapon_location_id = {self.kh2_loc_name_to_id[loc] for loc in all_weapon_slot}
368+
369+
try:
370+
if not self.kh2:
371+
self.kh2 = pymem.Pymem(process_name="KINGDOM HEARTS II FINAL MIX")
372+
self.get_addresses()
373+
374+
except Exception as e:
375+
if self.kh2connected:
376+
self.kh2connected = False
377+
logger.info("Game is not open.")
378+
self.serverconneced = True
379+
380+
def data_package_kh2_cache(self, loc_to_id, item_to_id):
381+
self.kh2_loc_name_to_id = loc_to_id
375382
self.lookup_id_to_location = {v: k for k, v in self.kh2_loc_name_to_id.items()}
376-
self.kh2_item_name_to_id = args["data"]["games"]["Kingdom Hearts 2"]["item_name_to_id"]
383+
self.kh2_item_name_to_id = item_to_id
377384
self.lookup_id_to_item = {v: k for k, v in self.kh2_item_name_to_id.items()}
378385
self.ability_code_list = [self.kh2_item_name_to_id[item] for item in exclusion_item_table["Ability"]]
379386

@@ -742,7 +749,8 @@ async def verifyItems(self):
742749
for item_name in master_stat:
743750
amount_of_items = 0
744751
amount_of_items += self.kh2_seed_save_cache["AmountInvo"]["StatIncrease"][item_name]
745-
if self.kh2_read_byte(self.Slot1 + 0x1B2) >= 5:
752+
# checking if they talked to the computer to give them these
753+
if self.kh2_read_byte(self.Slot1 + 0x1B2) >= 5 and (self.kh2_read_byte(self.Save + 0x1D27) & 0x1 << 3) > 0:
746754
if item_name == ItemName.MaxHPUp:
747755
if self.kh2_read_byte(self.Save + 0x2498) < 3: # Non-Critical
748756
Bonus = 5
@@ -808,34 +816,33 @@ async def verifyItems(self):
808816
def get_addresses(self):
809817
if not self.kh2connected and self.kh2 is not None:
810818
if self.kh2_game_version is None:
811-
812-
if self.kh2_read_string(0x09A9830, 4) == "KH2J":
819+
# current verions is .10 then runs the get from github stuff
820+
if self.kh2_read_string(0x9A98B0, 4) == "KH2J":
813821
self.kh2_game_version = "STEAM"
814822
self.Now = 0x0717008
815-
self.Save = 0x09A9830
816-
self.Slot1 = 0x2A23518
823+
self.Save = 0x09A98B0
824+
self.Slot1 = 0x2A23598
817825
self.Journal = 0x7434E0
818826
self.Shop = 0x7435D0
819-
elif self.kh2_read_string(0x09A92F0, 4) == "KH2J":
827+
elif self.kh2_read_string(0x9A9330, 4) == "KH2J":
820828
self.kh2_game_version = "EGS"
821829
else:
822830
if self.game_communication_path:
823-
logger.info("Checking with most up to date addresses of github. If file is not found will be downloading datafiles. This might take a moment")
831+
logger.info("Checking with most up to date addresses from the addresses json.")
824832
#if mem addresses file is found then check version and if old get new one
825-
kh2memaddresses_path = os.path.join(self.game_communication_path, f"kh2memaddresses.json")
833+
kh2memaddresses_path = os.path.join(self.game_communication_path, "kh2memaddresses.json")
826834
if not os.path.exists(kh2memaddresses_path):
835+
logger.info("File is not found. Downloading json with memory addresses. This might take a moment")
827836
mem_resp = requests.get("https://raw.githubusercontent.com/JaredWeakStrike/KH2APMemoryValues/master/kh2memaddresses.json")
828837
if mem_resp.status_code == 200:
829838
self.mem_json = json.loads(mem_resp.content)
830-
with open(kh2memaddresses_path,
831-
'w') as f:
839+
with open(kh2memaddresses_path, 'w') as f:
832840
f.write(json.dumps(self.mem_json, indent=4))
833841
else:
834-
with open(kh2memaddresses_path, 'r') as f:
842+
with open(kh2memaddresses_path) as f:
835843
self.mem_json = json.load(f)
836844
if self.mem_json:
837845
for key in self.mem_json.keys():
838-
839846
if self.kh2_read_string(int(self.mem_json[key]["GameVersionCheck"], 0), 4) == "KH2J":
840847
self.Now = int(self.mem_json[key]["Now"], 0)
841848
self.Save = int(self.mem_json[key]["Save"], 0)

worlds/kh2/OpenKH.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,37 @@ def increaseStat(i):
368368
}
369369
]
370370
},
371+
{
372+
'name': 'msg/us/he.bar',
373+
'multi': [
374+
{
375+
'name': 'msg/fr/he.bar'
376+
},
377+
{
378+
'name': 'msg/gr/he.bar'
379+
},
380+
{
381+
'name': 'msg/it/he.bar'
382+
},
383+
{
384+
'name': 'msg/sp/he.bar'
385+
}
386+
],
387+
'method': 'binarc',
388+
'source': [
389+
{
390+
'name': 'he',
391+
'type': 'list',
392+
'method': 'kh2msg',
393+
'source': [
394+
{
395+
'name': 'he.yml',
396+
'language': 'en'
397+
}
398+
]
399+
}
400+
]
401+
},
371402
],
372403
'title': 'Randomizer Seed'
373404
}
@@ -411,6 +442,34 @@ def increaseStat(i):
411442
'en': f"Your Level Depth is {self.options.LevelDepth.current_option_name}"
412443
}
413444
]
445+
self.fight_and_form_text = [
446+
{
447+
'id': 15121, # poster name
448+
'en': f"Game Options"
449+
},
450+
{
451+
'id': 15122,
452+
'en': f"Fight Logic is {self.options.FightLogic.current_option_name}\n"
453+
f"Auto Form Logic is {self.options.AutoFormLogic.current_option_name}\n"
454+
f"Final Form Logic is {self.options.FinalFormLogic.current_option_name}"
455+
}
456+
457+
]
458+
self.cups_text = [
459+
{
460+
'id': 4043,
461+
'en': f"CupsToggle: {self.options.Cups.current_option_name}"
462+
},
463+
{
464+
'id': 4044,
465+
'en': f"CupsToggle: {self.options.Cups.current_option_name}"
466+
},
467+
{
468+
'id': 4045,
469+
'en': f"CupsToggle: {self.options.Cups.current_option_name}"
470+
},
471+
]
472+
414473
mod_dir = os.path.join(output_directory, mod_name + "_" + Utils.__version__)
415474

416475
self.mod_yml["title"] = f"Randomizer Seed {mod_name}"
@@ -423,7 +482,8 @@ def increaseStat(i):
423482
"FmlvList.yml": yaml.dump(self.formattedFmlv, line_break="\n"),
424483
"mod.yml": yaml.dump(self.mod_yml, line_break="\n"),
425484
"po.yml": yaml.dump(self.pooh_text, line_break="\n"),
426-
"sys.yml": yaml.dump(self.level_depth_text, line_break="\n"),
485+
"sys.yml": yaml.dump(self.level_depth_text + self.fight_and_form_text, line_break="\n"),
486+
"he.yml": yaml.dump(self.cups_text, line_break="\n")
427487
}
428488

429489
mod = KH2Container(openkhmod, mod_dir, output_directory, self.player,

0 commit comments

Comments
 (0)