KH2: client bug fixes (#1742)
Use item index instead of location and player to determine if the player should get the item. Fixed getting stat increases on the title screen breaking stuff. Changed local locations list from a list organized by world-id to a set. Fixed the inventory slots to be the actual back of inventory. Fixed recaching when not closing the client but switching slots . Fixed getting a ability faster than the game so it dupes. Removed verify location since it was never used.
This commit is contained in:
parent
e1f17fadfc
commit
4ef7e43521
231
KH2Client.py
231
KH2Client.py
|
@ -53,79 +53,8 @@ class KH2Context(CommonContext):
|
|||
self.collectible_override_flags_address = 0
|
||||
self.collectible_offsets = {}
|
||||
self.sending = []
|
||||
# flag for if the player has gotten their starting inventory from the server
|
||||
self.hasStartingInvo = False
|
||||
# list used to keep track of locations+items player has. Used for disoneccting
|
||||
self.kh2seedsave = {"checked_locations": {"0": []},
|
||||
"starting_inventory": self.hasStartingInvo,
|
||||
|
||||
# Character: [back of invo, front of invo]
|
||||
"SoraInvo": [0x25CC, 0x2546],
|
||||
"DonaldInvo": [0x2678, 0x2658],
|
||||
"GoofyInvo": [0x278E, 0x276C],
|
||||
"AmountInvo": {
|
||||
"ServerItems": {
|
||||
"Ability": {},
|
||||
"Amount": {},
|
||||
"Growth": {"High Jump": 0, "Quick Run": 0, "Dodge Roll": 0, "Aerial Dodge": 0,
|
||||
"Glide": 0},
|
||||
"Bitmask": [],
|
||||
"Weapon": {"Sora": [], "Donald": [], "Goofy": []},
|
||||
"Equipment": [],
|
||||
"Magic": {},
|
||||
"StatIncrease": {},
|
||||
"Boost": {},
|
||||
},
|
||||
"LocalItems": {
|
||||
"Ability": {},
|
||||
"Amount": {},
|
||||
"Growth": {"High Jump": 0, "Quick Run": 0, "Dodge Roll": 0,
|
||||
"Aerial Dodge": 0, "Glide": 0},
|
||||
"Bitmask": [],
|
||||
"Weapon": {"Sora": [], "Donald": [], "Goofy": []},
|
||||
"Equipment": [],
|
||||
"Magic": {},
|
||||
"StatIncrease": {},
|
||||
"Boost": {},
|
||||
}},
|
||||
# 1,3,255 are in this list in case the player gets locations in those "worlds" and I need to still have them checked
|
||||
"worldIdChecks": {
|
||||
"1": [], # world of darkness (story cutscenes)
|
||||
"2": [],
|
||||
"3": [], # destiny island doesn't have checks to ima put tt checks here
|
||||
"4": [],
|
||||
"5": [],
|
||||
"6": [],
|
||||
"7": [],
|
||||
"8": [],
|
||||
"9": [],
|
||||
"10": [],
|
||||
"11": [],
|
||||
# atlantica isn't a supported world. if you go in atlantica it will check dc
|
||||
"12": [],
|
||||
"13": [],
|
||||
"14": [],
|
||||
"15": [],
|
||||
# world map, but you only go to the world map while on the way to goa so checking hb
|
||||
"16": [],
|
||||
"17": [],
|
||||
"18": [],
|
||||
"255": [], # starting screen
|
||||
},
|
||||
"Levels": {
|
||||
"SoraLevel": 0,
|
||||
"ValorLevel": 0,
|
||||
"WisdomLevel": 0,
|
||||
"LimitLevel": 0,
|
||||
"MasterLevel": 0,
|
||||
"FinalLevel": 0,
|
||||
},
|
||||
"SoldEquipment": [],
|
||||
"SoldBoosts": {"Power Boost": 0,
|
||||
"Magic Boost": 0,
|
||||
"Defense Boost": 0,
|
||||
"AP Boost": 0}
|
||||
}
|
||||
self.kh2seedsave = None
|
||||
self.slotDataProgressionNames = {}
|
||||
self.kh2seedname = None
|
||||
self.kh2slotdata = None
|
||||
|
@ -202,14 +131,13 @@ class KH2Context(CommonContext):
|
|||
|
||||
self.boost_set = set(CheckDupingItems["Boosts"])
|
||||
self.stat_increase_set = set(CheckDupingItems["Stat Increases"])
|
||||
|
||||
self.AbilityQuantityDict = {item: self.item_name_to_data[item].quantity for item in self.all_abilities}
|
||||
# Growth:[level 1,level 4,slot]
|
||||
self.growth_values_dict = {"High Jump": [0x05E, 0x061, 0x25CE],
|
||||
"Quick Run": [0x62, 0x65, 0x25D0],
|
||||
"Dodge Roll": [0x234, 0x237, 0x25D2],
|
||||
"Aerial Dodge": [0x066, 0x069, 0x25D4],
|
||||
"Glide": [0x6A, 0x6D, 0x25D6]}
|
||||
self.growth_values_dict = {"High Jump": [0x05E, 0x061, 0x25DA],
|
||||
"Quick Run": [0x62, 0x65, 0x25DC],
|
||||
"Dodge Roll": [0x234, 0x237, 0x25DE],
|
||||
"Aerial Dodge": [0x066, 0x069, 0x25E0],
|
||||
"Glide": [0x6A, 0x6D, 0x25E2]}
|
||||
self.boost_to_anchor_dict = {
|
||||
"Power Boost": 0x24F9,
|
||||
"Magic Boost": 0x24FA,
|
||||
|
@ -269,19 +197,66 @@ class KH2Context(CommonContext):
|
|||
if not os.path.exists(self.game_communication_path):
|
||||
os.makedirs(self.game_communication_path)
|
||||
if not os.path.exists(self.game_communication_path + f"\kh2save{self.kh2seedname}{self.auth}.json"):
|
||||
self.kh2seedsave = {"itemIndex": -1,
|
||||
# back of soras invo is 0x25E2. Growth should be moved there
|
||||
# Character: [back of invo, front of invo]
|
||||
"SoraInvo": [0x25D8, 0x2546],
|
||||
"DonaldInvo": [0x26F4, 0x2658],
|
||||
"GoofyInvo": [0x280A, 0x276C],
|
||||
"AmountInvo": {
|
||||
"ServerItems": {
|
||||
"Ability": {},
|
||||
"Amount": {},
|
||||
"Growth": {"High Jump": 0, "Quick Run": 0, "Dodge Roll": 0,
|
||||
"Aerial Dodge": 0,
|
||||
"Glide": 0},
|
||||
"Bitmask": [],
|
||||
"Weapon": {"Sora": [], "Donald": [], "Goofy": []},
|
||||
"Equipment": [],
|
||||
"Magic": {},
|
||||
"StatIncrease": {},
|
||||
"Boost": {},
|
||||
},
|
||||
"LocalItems": {
|
||||
"Ability": {},
|
||||
"Amount": {},
|
||||
"Growth": {"High Jump": 0, "Quick Run": 0, "Dodge Roll": 0,
|
||||
"Aerial Dodge": 0, "Glide": 0},
|
||||
"Bitmask": [],
|
||||
"Weapon": {"Sora": [], "Donald": [], "Goofy": []},
|
||||
"Equipment": [],
|
||||
"Magic": {},
|
||||
"StatIncrease": {},
|
||||
"Boost": {},
|
||||
}},
|
||||
# 1,3,255 are in this list in case the player gets locations in those "worlds" and I need to still have them checked
|
||||
"LocationsChecked": [],
|
||||
"Levels": {
|
||||
"SoraLevel": 0,
|
||||
"ValorLevel": 0,
|
||||
"WisdomLevel": 0,
|
||||
"LimitLevel": 0,
|
||||
"MasterLevel": 0,
|
||||
"FinalLevel": 0,
|
||||
},
|
||||
"SoldEquipment": [],
|
||||
"SoldBoosts": {"Power Boost": 0,
|
||||
"Magic Boost": 0,
|
||||
"Defense Boost": 0,
|
||||
"AP Boost": 0}
|
||||
}
|
||||
with open(os.path.join(self.game_communication_path, f"kh2save{self.kh2seedname}{self.auth}.json"),
|
||||
'wt') as f:
|
||||
pass
|
||||
self.locations_checked = set()
|
||||
elif os.path.exists(self.game_communication_path + f"\kh2save{self.kh2seedname}{self.auth}.json"):
|
||||
with open(self.game_communication_path + f"\kh2save{self.kh2seedname}{self.auth}.json", 'r') as f:
|
||||
self.kh2seedsave = json.load(f)
|
||||
self.locations_checked = set(self.kh2seedsave["LocationsChecked"])
|
||||
self.serverconneced = True
|
||||
|
||||
if cmd in {"Connected"}:
|
||||
for player in args['players']:
|
||||
if str(player.slot) not in self.kh2seedsave["checked_locations"]:
|
||||
self.kh2seedsave["checked_locations"].update({str(player.slot): []})
|
||||
self.kh2slotdata = args['slot_data']
|
||||
self.serverconneced = True
|
||||
self.kh2LocalItems = {int(location): item for location, item in self.kh2slotdata["LocalItems"].items()}
|
||||
try:
|
||||
self.kh2 = pymem.Pymem(process_name="KINGDOM HEARTS II FINAL MIX")
|
||||
|
@ -296,21 +271,10 @@ class KH2Context(CommonContext):
|
|||
|
||||
if cmd in {"ReceivedItems"}:
|
||||
start_index = args["index"]
|
||||
if start_index != len(self.items_received):
|
||||
if start_index > self.kh2seedsave["itemIndex"]:
|
||||
self.kh2seedsave["itemIndex"] = start_index
|
||||
for item in args['items']:
|
||||
# starting invo from server
|
||||
if item.location in {-2}:
|
||||
if not self.kh2seedsave["starting_inventory"]:
|
||||
asyncio.create_task(self.give_item(item.item))
|
||||
# if location is not already given or is !getitem
|
||||
elif item.location not in self.kh2seedsave["checked_locations"][str(item.player)] \
|
||||
or item.location in {-1}:
|
||||
asyncio.create_task(self.give_item(item.item))
|
||||
if item.location not in self.kh2seedsave["checked_locations"][str(item.player)] \
|
||||
and item.location not in {-1, -2}:
|
||||
self.kh2seedsave["checked_locations"][str(item.player)].append(item.location)
|
||||
if not self.kh2seedsave["starting_inventory"]:
|
||||
self.kh2seedsave["starting_inventory"] = True
|
||||
asyncio.create_task(self.give_item(item.item))
|
||||
|
||||
if cmd in {"RoomUpdate"}:
|
||||
if "checked_locations" in args:
|
||||
|
@ -326,12 +290,13 @@ class KH2Context(CommonContext):
|
|||
if currentworldint in self.worldid:
|
||||
curworldid = self.worldid[currentworldint]
|
||||
for location, data in curworldid.items():
|
||||
if location not in self.locations_checked \
|
||||
locationId = kh2_loc_name_to_id[location]
|
||||
if locationId not in self.locations_checked \
|
||||
and (int.from_bytes(
|
||||
self.kh2.read_bytes(self.kh2.base_address + self.Save + data.addrObtained, 1),
|
||||
"big") & 0x1 << data.bitIndex) > 0:
|
||||
self.locations_checked.add(location)
|
||||
self.sending = self.sending + [(int(kh2_loc_name_to_id[location]))]
|
||||
|
||||
self.sending = self.sending + [(int(locationId))]
|
||||
except Exception as e:
|
||||
logger.info("Line 285")
|
||||
if self.kh2connected:
|
||||
|
@ -344,12 +309,12 @@ class KH2Context(CommonContext):
|
|||
for location, data in SoraLevels.items():
|
||||
currentLevel = int.from_bytes(
|
||||
self.kh2.read_bytes(self.kh2.base_address + self.Save + 0x24FF, 1), "big")
|
||||
if location not in self.locations_checked \
|
||||
locationId = kh2_loc_name_to_id[location]
|
||||
if locationId not in self.locations_checked \
|
||||
and currentLevel >= data.bitIndex:
|
||||
if self.kh2seedsave["Levels"]["SoraLevel"] < currentLevel:
|
||||
self.kh2seedsave["Levels"]["SoraLevel"] = currentLevel
|
||||
self.locations_checked.add(location)
|
||||
self.sending = self.sending + [(int(kh2_loc_name_to_id[location]))]
|
||||
self.sending = self.sending + [(int(locationId))]
|
||||
formDict = {
|
||||
0: ["ValorLevel", ValorLevels], 1: ["WisdomLevel", WisdomLevels], 2: ["LimitLevel", LimitLevels],
|
||||
3: ["MasterLevel", MasterLevels], 4: ["FinalLevel", FinalLevels]}
|
||||
|
@ -357,12 +322,12 @@ class KH2Context(CommonContext):
|
|||
for location, data in formDict[i][1].items():
|
||||
formlevel = int.from_bytes(
|
||||
self.kh2.read_bytes(self.kh2.base_address + self.Save + data.addrObtained, 1), "big")
|
||||
if location not in self.locations_checked \
|
||||
locationId = kh2_loc_name_to_id[location]
|
||||
if locationId not in self.locations_checked \
|
||||
and formlevel >= data.bitIndex:
|
||||
if formlevel > self.kh2seedsave["Levels"][formDict[i][0]]:
|
||||
self.kh2seedsave["Levels"][formDict[i][0]] = formlevel
|
||||
self.locations_checked.add(location)
|
||||
self.sending = self.sending + [(int(kh2_loc_name_to_id[location]))]
|
||||
self.sending = self.sending + [(int(locationId))]
|
||||
except Exception as e:
|
||||
logger.info("Line 312")
|
||||
if self.kh2connected:
|
||||
|
@ -373,18 +338,21 @@ class KH2Context(CommonContext):
|
|||
async def checkSlots(self):
|
||||
try:
|
||||
for location, data in weaponSlots.items():
|
||||
if location not in self.locations_checked:
|
||||
locationId = kh2_loc_name_to_id[location]
|
||||
if locationId not in self.locations_checked:
|
||||
if int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + data.addrObtained, 1),
|
||||
"big") > 0:
|
||||
self.locations_checked.add(location)
|
||||
self.sending = self.sending + [(int(kh2_loc_name_to_id[location]))]
|
||||
|
||||
self.sending = self.sending + [(int(locationId))]
|
||||
|
||||
for location, data in formSlots.items():
|
||||
if location not in self.locations_checked:
|
||||
locationId = kh2_loc_name_to_id[location]
|
||||
if locationId not in self.locations_checked:
|
||||
if int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + data.addrObtained, 1),
|
||||
"big") & 0x1 << data.bitIndex > 0:
|
||||
self.locations_checked.add(location)
|
||||
self.sending = self.sending + [(int(kh2_loc_name_to_id[location]))]
|
||||
#self.locations_checked
|
||||
self.sending = self.sending + [(int(locationId))]
|
||||
|
||||
except Exception as e:
|
||||
if self.kh2connected:
|
||||
logger.info("Line 333")
|
||||
|
@ -394,8 +362,7 @@ class KH2Context(CommonContext):
|
|||
|
||||
async def verifyChests(self):
|
||||
try:
|
||||
currentworld = str(int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + 0x0714DB8, 1), "big"))
|
||||
for location in self.kh2seedsave["worldIdChecks"][currentworld]:
|
||||
for location in self.locations_checked:
|
||||
locationName = self.lookup_id_to_Location[location]
|
||||
if locationName in self.chest_set:
|
||||
if locationName in self.location_name_to_worlddata.keys():
|
||||
|
@ -428,24 +395,6 @@ class KH2Context(CommonContext):
|
|||
self.kh2.write_bytes(self.kh2.base_address + self.Save + anchor,
|
||||
(self.kh2seedsave["Levels"][leveltype]).to_bytes(1, 'big'), 1)
|
||||
|
||||
def verifyLocation(self, location):
|
||||
locationData = self.location_name_to_worlddata[location]
|
||||
locationName = self.lookup_id_to_Location[location]
|
||||
isChecked = True
|
||||
|
||||
if locationName not in levels_locations:
|
||||
if (int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + locationData.addrObtained, 1),
|
||||
"big") & 0x1 << locationData.bitIndex) == 0:
|
||||
isChecked = False
|
||||
elif locationName in SoraLevels:
|
||||
if int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + 0x24FF, 1),
|
||||
"big") < locationData.bitIndex:
|
||||
isChecked = False
|
||||
elif int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + locationData.addrObtained, 1),
|
||||
"big") < locationData.bitIndex:
|
||||
isChecked = False
|
||||
return isChecked
|
||||
|
||||
async def give_item(self, item, ItemType="ServerItems"):
|
||||
try:
|
||||
itemname = self.lookup_id_to_item[item]
|
||||
|
@ -680,6 +629,17 @@ class KH2Context(CommonContext):
|
|||
ability = current & 0x0FFF
|
||||
if ability | 0x8000 != (0x8000 + itemData.memaddr):
|
||||
self.kh2.write_short(self.kh2.base_address + self.Save + slot, itemData.memaddr)
|
||||
# removes the duped ability if client gave faster than the game.
|
||||
for charInvo in {"SoraInvo", "DonaldInvo", "GoofyInvo"}:
|
||||
if self.kh2.read_short(self.kh2.base_address + self.Save + self.kh2seedsave[charInvo][1]) != 0 and\
|
||||
self.kh2seedsave[charInvo][1]+2 < self.kh2seedsave[charInvo][0]:
|
||||
self.kh2.write_short(self.kh2.base_address + self.Save + self.kh2seedsave[charInvo][1], 0)
|
||||
# remove the dummy level 1 growths if they are in these invo slots.
|
||||
for inventorySlot in {0x25CE, 0x25D0, 0x25D2, 0x25D4, 0x25D6, 0x25D8}:
|
||||
current = self.kh2.read_short(self.kh2.base_address + self.Save + inventorySlot)
|
||||
ability = current & 0x0FFF
|
||||
if 0x05E <= ability <= 0x06D:
|
||||
self.kh2.write_short(self.kh2.base_address + self.Save + inventorySlot, 0)
|
||||
|
||||
for itemName in self.master_growth:
|
||||
growthLevel = self.kh2seedsave["AmountInvo"]["ServerItems"]["Growth"][itemName] \
|
||||
|
@ -753,10 +713,11 @@ class KH2Context(CommonContext):
|
|||
if itemName in server_stat:
|
||||
amountOfItems += self.kh2seedsave["AmountInvo"]["ServerItems"]["StatIncrease"][itemName]
|
||||
|
||||
# 0x130293 is Crit_1's location id for touching the computer
|
||||
if int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + itemData.memaddr, 1),
|
||||
"big") != amountOfItems \
|
||||
and int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Slot1 + 0x1B2, 1),
|
||||
"big") >= 5:
|
||||
"big") >= 5 and 0x130293 in self.locations_checked:
|
||||
self.kh2.write_bytes(self.kh2.base_address + self.Save + itemData.memaddr,
|
||||
amountOfItems.to_bytes(1, 'big'), 1)
|
||||
|
||||
|
@ -859,9 +820,9 @@ async def kh2_watcher(ctx: KH2Context):
|
|||
location_ids = []
|
||||
location_ids = [location for location in message[0]["locations"] if location not in location_ids]
|
||||
for location in location_ids:
|
||||
currentWorld = int.from_bytes(ctx.kh2.read_bytes(ctx.kh2.base_address + 0x0714DB8, 1), "big")
|
||||
if location not in ctx.kh2seedsave["worldIdChecks"][str(currentWorld)]:
|
||||
ctx.kh2seedsave["worldIdChecks"][str(currentWorld)].append(location)
|
||||
if location not in ctx.locations_checked:
|
||||
ctx.locations_checked.add(location)
|
||||
ctx.kh2seedsave["LocationsChecked"].append(location)
|
||||
if location in ctx.kh2LocalItems:
|
||||
item = ctx.kh2slotdata["LocalItems"][str(location)]
|
||||
await asyncio.create_task(ctx.give_item(item, "LocalItems"))
|
||||
|
|
Loading…
Reference in New Issue