Skip to content

Commit

Permalink
oot: bugfixes (ArchipelagoMW#1709)
Browse files Browse the repository at this point in the history
* oot client: check types of tables coming from lua script for safety
There was a reported bug with corrupted (?) slot data preventing locations sending. This should safeguard against any instances of that happening in the future, if it ever happens again.

* oot: repair minor hint issues
SMW has # in some location names which breaks ootr's poor text formatting system, so those need to be filtered out.
Also replaces "[X] for [player Y]" with "[player Y]'s X" as frequently requested.

* oot: update required client version

* oot client: fix patching filename bug

* oot: fix broken poachers saw item
how was I this stupid, seriously

* oot: sanitize player, location, and item names everywhere
  • Loading branch information
espeon65536 authored Apr 15, 2023
1 parent ef211da commit 89ec317
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 15 deletions.
19 changes: 15 additions & 4 deletions OoTClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,12 @@ async def parse_payload(payload: dict, ctx: OoTContext, force: bool):
locations = payload['locations']
collectibles = payload['collectibles']

# The Lua JSON library serializes an empty table into a list instead of a dict. Verify types for safety:
if isinstance(locations, list):
locations = {}
if isinstance(collectibles, list):
collectibles = {}

if ctx.location_table != locations or ctx.collectible_table != collectibles:
ctx.location_table = locations
ctx.collectible_table = collectibles
Expand Down Expand Up @@ -293,10 +299,15 @@ async def patch_and_run_game(apz5_file):
if not os.path.exists(rom_file_name):
rom_file_name = Utils.user_path(rom_file_name)
rom = Rom(rom_file_name)
apply_patch_file(rom, apz5_file,
sub_file=(os.path.basename(base_name) + '.zpf'
if zipfile.is_zipfile(apz5_file)
else None))

sub_file = None
if zipfile.is_zipfile(apz5_file):
for name in zipfile.ZipFile(apz5_file).namelist():
if name.endswith('.zpf'):
sub_file = name
break

apply_patch_file(rom, apz5_file, sub_file=sub_file)
rom.write_to_file(decomp_path)
os.chdir(data_path("Compress"))
compress_rom_file(decomp_path, comp_path)
Expand Down
10 changes: 5 additions & 5 deletions worlds/oot/Hints.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from .HintList import getHint, getHintGroup, Hint, hintExclusions, \
misc_item_hint_table, misc_location_hint_table
from .Messages import COLOR_MAP, update_message_by_id
from .TextBox import line_wrap
from .TextBox import line_wrap, character_table, rom_safe_text
from .Utils import data_path, read_json


Expand Down Expand Up @@ -149,11 +149,11 @@ def isRestrictedDungeonItem(dungeon, item):

# Attach a player name to the item or location text.
# If the associated player of the item/location and the world are the same, does nothing.
# Otherwise, attaches the object's player's name to the string.
# Otherwise, attaches the object's player's name to the string, calling rom_safe_text for foreign items/locations.
def attach_name(text, hinted_object, world):
if hinted_object.player == world.player:
return text
return f"{text} for {world.multiworld.get_player_name(hinted_object.player)}"
return rom_safe_text(f"{world.multiworld.get_player_name(hinted_object.player)}'s {text}")


def add_hint(world, groups, gossip_text, count, location=None, force_reachable=False):
Expand Down Expand Up @@ -1144,7 +1144,7 @@ def buildMiscItemHints(world, messages):
area = HintArea.at(location, use_alt_hint=data['use_alt_hint']).text(world.clearer_hints, world=None)
else:
area = location.name
text = data['default_item_text'].format(area=(player_text + area))
text = data['default_item_text'].format(area=rom_safe_text(player_text + area))
elif 'default_item_fallback' in data:
text = data['default_item_fallback']
else:
Expand All @@ -1167,7 +1167,7 @@ def buildMiscLocationHints(world, messages):
item_text = getHint(getItemGenericName(item), world.clearer_hints).text
if item.player != world.player:
item_text += f' for {world.multiworld.get_player_name(item.player)}'
text = data['location_text'].format(item=item_text)
text = data['location_text'].format(item=rom_safe_text(item_text))

update_message_by_id(messages, data['id'], str(GossipText(text, ['Green'], prefix='')), 0x23)

Expand Down
2 changes: 1 addition & 1 deletion worlds/oot/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ class AdultTradeStart(OptionSet):
"Pocket Cucco",
"Cojiro",
"Odd Mushroom",
"Poacher's Saw",
"Poachers Saw",
"Broken Sword",
"Prescription",
"Eyeball Frog",
Expand Down
8 changes: 4 additions & 4 deletions worlds/oot/Patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from .SaveContext import SaveContext, Scenes, FlagType
from .SceneFlags import get_alt_list_bytes, get_collectible_flag_table, get_collectible_flag_table_bytes, \
get_collectible_flag_addresses
from .TextBox import character_table, NORMAL_LINE_WIDTH
from .TextBox import character_table, NORMAL_LINE_WIDTH, rom_safe_text
from .texture_util import ci4_rgba16patch_to_ci8, rgba16_patch
from .Utils import __version__

Expand Down Expand Up @@ -2771,7 +2771,7 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F
split_item_name[0] = create_fake_name(split_item_name[0])

if len(world.multiworld.worlds) > 1: # OOTWorld.MultiWorld.AutoWorld[]
description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1], world.multiworld.get_player_name(location.item.player))
description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1], rom_safe_text(world.multiworld.get_player_name(location.item.player)))
else:
description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x40Special deal! ONE LEFT!\x01Get it while it lasts!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1])
purchase_text = '\x08%s %d Rupees\x09\x01%s\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02' % (split_item_name[0], location.price, split_item_name[1])
Expand All @@ -2785,9 +2785,9 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F
shop_item_name = create_fake_name(shop_item_name)

if len(world.multiworld.worlds) > 1:
shop_item_name = ''.join(filter(lambda char: char in character_table, shop_item_name))
shop_item_name = rom_safe_text(shop_item_name)
do_line_break = sum(character_table[char] for char in f"{shop_item_name} {location.price} Rupees") > NORMAL_LINE_WIDTH
description_text = '\x08\x05\x41%s%s%d Rupees\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (shop_item_name, '\x01' if do_line_break else ' ', location.price, world.multiworld.get_player_name(location.item.player))
description_text = '\x08\x05\x41%s%s%d Rupees\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (shop_item_name, '\x01' if do_line_break else ' ', location.price, rom_safe_text(world.multiworld.get_player_name(location.item.player)))
else:
description_text = '\x08\x05\x41%s %d Rupees\x01\x05\x40Special deal! ONE LEFT!\x01Get it while it lasts!\x09\x0A\x02' % (shop_item_name, location.price)
purchase_text = '\x08%s %d Rupees\x09\x01\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02' % (shop_item_name, location.price)
Expand Down
7 changes: 7 additions & 0 deletions worlds/oot/TextBox.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,10 @@ def test_support_long_words():
print('"Support Long Words" test failed: Got ' + result + ', wanted ' + expected)
else:
print('"Support Long Words" test passed!')


# AP additions

rom_safe_lambda = lambda c: c if c in character_table else '?'
def rom_safe_text(text):
return ''.join(map(rom_safe_lambda, text))
2 changes: 1 addition & 1 deletion worlds/oot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class OOTWorld(World):

data_version = 3

required_client_version = (0, 3, 7)
required_client_version = (0, 4, 0)

item_name_groups = {
# internal groups
Expand Down

0 comments on commit 89ec317

Please sign in to comment.