From d2fbd89a47fb7a616b709f6fa8ea2d570ed89dcf Mon Sep 17 00:00:00 2001 From: Phar Date: Mon, 13 Nov 2023 09:12:42 -0600 Subject: [PATCH] Additional edge cases, variable name changes, and some tweaks to UI. --- WebHostLib/options.py | 29 ++++++++++-- WebHostLib/templates/player-options.html | 3 +- docs/world api.md | 8 ++-- test/webhost/test_option_presets.py | 59 ++++++++++++++++++++---- worlds/rogue_legacy/Presets.py | 17 +++---- worlds/rogue_legacy/__init__.py | 4 +- 6 files changed, 93 insertions(+), 27 deletions(-) diff --git a/WebHostLib/options.py b/WebHostLib/options.py index fb1a637111d1..49146c9ddb34 100644 --- a/WebHostLib/options.py +++ b/WebHostLib/options.py @@ -119,17 +119,40 @@ def get_html_doc(option_type: type(Options.Option)) -> str: player_options["gameOptions"] = game_options player_options["presetOptions"] = {} - for preset_name, presets in world.web.options_presets.items(): + for preset_name, preset in world.web.options_presets.items(): player_options["presetOptions"][preset_name] = {} - for option_name, option_value in presets.items(): + for option_name, option_value in preset.items(): + # Random range type settings are not valid. + assert (option_value not in ["random-low", "random-high", "random-middle"] and + not str(option_value).startswith("random-")), \ + f"Invalid preset value '{option_value}' for '{option_name}' in '{preset_name}'. Special random " \ + f"values are not supported for presets." + + # Normal random is supported, but needs to be handled explicitly. if option_value == "random": player_options["presetOptions"][preset_name][option_name] = option_value continue option = world.options_dataclass.type_hints[option_name].from_any(option_value) - if issubclass(option.__class__, Options.Range): + if isinstance(option, Options.SpecialRange) and isinstance(option_value, str): + assert option_value in option.special_range_names, \ + f"Invalid preset value '{option_value}' for '{option_name}' in '{preset_name}'. " \ + f"Expected {option.special_range_names.keys()} or {option.range_start}-{option.range_end}." + + # Still use the true value for the option, not the name. + player_options["presetOptions"][preset_name][option_name] = option.value + elif isinstance(option, Options.Range): player_options["presetOptions"][preset_name][option_name] = option.value + elif isinstance(option_value, str): + # For Choice and Toggle options, the value should be the name of the option. This is to prevent + # setting a preset for an option with an overridden from_text method that would normally be okay, + # but would not be okay for the webhost's current implementation of player options UI. + assert option.name_lookup[option.value] == option_value, \ + f"Invalid option value '{option_value}' for '{option_name}' in preset '{preset_name}'. " \ + f"Values must not be resolved to a different option via option.from_text (or an alias)." + player_options["presetOptions"][preset_name][option_name] = option.current_key else: + # int and bool values are fine, just resolve them to the current key for webhost. player_options["presetOptions"][preset_name][option_name] = option.current_key os.makedirs(os.path.join(target_folder, 'player-options'), exist_ok=True) diff --git a/WebHostLib/templates/player-options.html b/WebHostLib/templates/player-options.html index 7c35241ecbb4..4c749752882a 100644 --- a/WebHostLib/templates/player-options.html +++ b/WebHostLib/templates/player-options.html @@ -28,7 +28,6 @@

Player Options

template file for this game.

-

Meta Options