oot: bugfixes (#1709)
* 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
This commit is contained in:
parent
ef211da27f
commit
89ec31708e
19
OoTClient.py
19
OoTClient.py
|
@ -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
|
||||
|
@ -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)
|
||||
|
|
|
@ -15,7 +15,7 @@ from .Items import OOTItem
|
|||
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
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
||||
|
|
|
@ -1037,7 +1037,7 @@ class AdultTradeStart(OptionSet):
|
|||
"Pocket Cucco",
|
||||
"Cojiro",
|
||||
"Odd Mushroom",
|
||||
"Poacher's Saw",
|
||||
"Poachers Saw",
|
||||
"Broken Sword",
|
||||
"Prescription",
|
||||
"Eyeball Frog",
|
||||
|
|
|
@ -25,7 +25,7 @@ from .Rom import Rom
|
|||
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__
|
||||
|
||||
|
@ -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])
|
||||
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue