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']
|
locations = payload['locations']
|
||||||
collectibles = payload['collectibles']
|
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:
|
if ctx.location_table != locations or ctx.collectible_table != collectibles:
|
||||||
ctx.location_table = locations
|
ctx.location_table = locations
|
||||||
ctx.collectible_table = collectibles
|
ctx.collectible_table = collectibles
|
||||||
|
@ -293,10 +299,15 @@ async def patch_and_run_game(apz5_file):
|
||||||
if not os.path.exists(rom_file_name):
|
if not os.path.exists(rom_file_name):
|
||||||
rom_file_name = Utils.user_path(rom_file_name)
|
rom_file_name = Utils.user_path(rom_file_name)
|
||||||
rom = Rom(rom_file_name)
|
rom = Rom(rom_file_name)
|
||||||
apply_patch_file(rom, apz5_file,
|
|
||||||
sub_file=(os.path.basename(base_name) + '.zpf'
|
sub_file = None
|
||||||
if zipfile.is_zipfile(apz5_file)
|
if zipfile.is_zipfile(apz5_file):
|
||||||
else None))
|
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)
|
rom.write_to_file(decomp_path)
|
||||||
os.chdir(data_path("Compress"))
|
os.chdir(data_path("Compress"))
|
||||||
compress_rom_file(decomp_path, comp_path)
|
compress_rom_file(decomp_path, comp_path)
|
||||||
|
|
|
@ -15,7 +15,7 @@ from .Items import OOTItem
|
||||||
from .HintList import getHint, getHintGroup, Hint, hintExclusions, \
|
from .HintList import getHint, getHintGroup, Hint, hintExclusions, \
|
||||||
misc_item_hint_table, misc_location_hint_table
|
misc_item_hint_table, misc_location_hint_table
|
||||||
from .Messages import COLOR_MAP, update_message_by_id
|
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
|
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.
|
# 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.
|
# 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):
|
def attach_name(text, hinted_object, world):
|
||||||
if hinted_object.player == world.player:
|
if hinted_object.player == world.player:
|
||||||
return text
|
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):
|
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)
|
area = HintArea.at(location, use_alt_hint=data['use_alt_hint']).text(world.clearer_hints, world=None)
|
||||||
else:
|
else:
|
||||||
area = location.name
|
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:
|
elif 'default_item_fallback' in data:
|
||||||
text = data['default_item_fallback']
|
text = data['default_item_fallback']
|
||||||
else:
|
else:
|
||||||
|
@ -1167,7 +1167,7 @@ def buildMiscLocationHints(world, messages):
|
||||||
item_text = getHint(getItemGenericName(item), world.clearer_hints).text
|
item_text = getHint(getItemGenericName(item), world.clearer_hints).text
|
||||||
if item.player != world.player:
|
if item.player != world.player:
|
||||||
item_text += f' for {world.multiworld.get_player_name(item.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)
|
update_message_by_id(messages, data['id'], str(GossipText(text, ['Green'], prefix='')), 0x23)
|
||||||
|
|
||||||
|
|
|
@ -1037,7 +1037,7 @@ class AdultTradeStart(OptionSet):
|
||||||
"Pocket Cucco",
|
"Pocket Cucco",
|
||||||
"Cojiro",
|
"Cojiro",
|
||||||
"Odd Mushroom",
|
"Odd Mushroom",
|
||||||
"Poacher's Saw",
|
"Poachers Saw",
|
||||||
"Broken Sword",
|
"Broken Sword",
|
||||||
"Prescription",
|
"Prescription",
|
||||||
"Eyeball Frog",
|
"Eyeball Frog",
|
||||||
|
|
|
@ -25,7 +25,7 @@ from .Rom import Rom
|
||||||
from .SaveContext import SaveContext, Scenes, FlagType
|
from .SaveContext import SaveContext, Scenes, FlagType
|
||||||
from .SceneFlags import get_alt_list_bytes, get_collectible_flag_table, get_collectible_flag_table_bytes, \
|
from .SceneFlags import get_alt_list_bytes, get_collectible_flag_table, get_collectible_flag_table_bytes, \
|
||||||
get_collectible_flag_addresses
|
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 .texture_util import ci4_rgba16patch_to_ci8, rgba16_patch
|
||||||
from .Utils import __version__
|
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])
|
split_item_name[0] = create_fake_name(split_item_name[0])
|
||||||
|
|
||||||
if len(world.multiworld.worlds) > 1: # OOTWorld.MultiWorld.AutoWorld[]
|
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:
|
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])
|
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])
|
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)
|
shop_item_name = create_fake_name(shop_item_name)
|
||||||
|
|
||||||
if len(world.multiworld.worlds) > 1:
|
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
|
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:
|
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)
|
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)
|
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)
|
print('"Support Long Words" test failed: Got ' + result + ', wanted ' + expected)
|
||||||
else:
|
else:
|
||||||
print('"Support Long Words" test passed!')
|
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
|
data_version = 3
|
||||||
|
|
||||||
required_client_version = (0, 3, 7)
|
required_client_version = (0, 4, 0)
|
||||||
|
|
||||||
item_name_groups = {
|
item_name_groups = {
|
||||||
# internal groups
|
# internal groups
|
||||||
|
|
Loading…
Reference in New Issue